19 #ifdef HAVE_VORBISFILE
20 #include "decoders/vorbisfile.hpp"
23 #include "decoders/flac.hpp"
26 #include "decoders/opusfile.hpp"
28 #ifdef HAVE_LIBSNDFILE
29 #include "decoders/sndfile.hpp"
32 #include "decoders/mpg123.hpp"
34 #include "decoders/wave.hpp"
36 #include "devicemanager.h"
40 #include "auxeffectslot.h"
42 #include "sourcegroup.h"
45 #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
;
92 // Global mutex to protect global context changes
93 std::mutex mGlobalCtxMutex
;
96 // Windows' std::ifstream fails with non-ANSI paths since the standard only
97 // specifies names using const char* (or std::string). MSVC has a non-standard
98 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
99 // but not all Windows compilers support it. So we have to make our own istream
100 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
101 class StreamBuf final
: public std::streambuf
{
102 alure::Array
<char_type
,4096> mBuffer
;
105 int_type
underflow() override
107 if(mFile
!= INVALID_HANDLE_VALUE
&& gptr() == egptr())
109 // Read in the next chunk of data, and set the pointers on success
111 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 { return mFile
!= INVALID_HANDLE_VALUE
; }
204 StreamBuf() : mFile(INVALID_HANDLE_VALUE
)
206 ~StreamBuf() override
208 if(mFile
!= INVALID_HANDLE_VALUE
)
210 mFile
= INVALID_HANDLE_VALUE
;
214 // Inherit from std::istream to use our custom streambuf
215 class Stream final
: public std::istream
{
217 Stream(const char *filename
) : std::istream(new StreamBuf())
219 // Set the failbit if the file failed to open.
220 if(!(static_cast<StreamBuf
*>(rdbuf())->open(filename
)))
226 bool is_open() const { return static_cast<StreamBuf
*>(rdbuf())->is_open(); }
230 using DecoderEntryPair
= std::pair
<alure::String
,alure::UniquePtr
<alure::DecoderFactory
>>;
231 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
;
254 alure::DecoderOrExceptT
GetDecoder(alure::UniquePtr
<std::istream
> &file
, T start
, T end
)
256 alure::DecoderOrExceptT ret
;
259 alure::DecoderFactory
*factory
= start
->second
.get();
260 auto decoder
= factory
->createDecoder(file
);
261 if(decoder
) return (ret
= std::move(decoder
));
263 if(!file
|| !(file
->clear(),file
->seekg(0)))
264 return (ret
= std::make_exception_ptr(std::runtime_error(
265 "Failed to rewind file for the next decoder factory"
271 return (ret
= alure::SharedPtr
<alure::Decoder
>(nullptr));
274 static alure::DecoderOrExceptT
GetDecoder(alure::UniquePtr
<std::istream
> file
)
276 auto decoder
= GetDecoder(file
, sDecoders
.begin(), sDecoders
.end());
277 if(std::holds_alternative
<std::exception_ptr
>(decoder
)) return decoder
;
278 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
279 decoder
= GetDecoder(file
, std::begin(sDefaultDecoders
), std::end(sDefaultDecoders
));
280 if(std::holds_alternative
<std::exception_ptr
>(decoder
)) return decoder
;
281 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
282 return (decoder
= std::make_exception_ptr(std::runtime_error("No decoder found")));
285 class DefaultFileIOFactory final
: public alure::FileIOFactory
{
286 alure::UniquePtr
<std::istream
> openFile(const alure::String
&name
) noexcept override
289 auto file
= alure::MakeUnique
<Stream
>(name
.c_str());
291 auto file
= alure::MakeUnique
<std::ifstream
>(name
.c_str(), std::ios::binary
);
293 if(!file
->is_open()) file
= nullptr;
294 return std::move(file
);
297 DefaultFileIOFactory sDefaultFileFactory
;
299 alure::UniquePtr
<alure::FileIOFactory
> sFileFactory
;
306 std::variant
<std::monostate
,uint64_t> ParseTimeval(StringView strval
, double srate
)
308 size_t cpos
= strval
.find_first_of(':');
309 if(cpos
== StringView::npos
)
311 // No colon is present, treat it as a plain sample offset
313 auto str
= String(strval
);
314 uint64_t val
= std::strtoull(str
.c_str(), &end
, 10);
315 if(*end
!= 0) return {};
319 // Value is not a sample offset. Its format is [[HH:]MM]:SS[.sss] (at
320 // least one colon must exist to be interpreted this way).
327 // If a non-empty first value, parse it (may be hours or minutes)
328 str
= String(strval
.data(), cpos
);
329 val
= std::strtoul(str
.c_str(), &end
, 10);
330 if(*end
!= 0) return {};
333 strval
= strval
.substr(cpos
+1);
334 cpos
= strval
.find_first_of(':');
335 if(cpos
!= StringView::npos
)
337 // If a second colon is present, the first value was hours and this is
338 // minutes, otherwise the first value was minutes.
339 str
= String(strval
.data(), cpos
);
340 uint64_t val2
= std::strtoul(str
.c_str(), &end
, 10);
341 if(*end
!= 0 || 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 if(dpos
== StringView::npos
)
357 str
= String(strval
);
359 str
= String(strval
.substr(0, dpos
+4));
360 secs
= std::strtod(str
.c_str(), &end
);
361 if(*end
!= 0 || !(secs
>= 0.0 && secs
< 60.0))
365 // Convert minutes to seconds, add the seconds, then convert to samples.
366 return static_cast<uint64_t>((val
*60.0 + secs
) * srate
);
370 Decoder::~Decoder() { }
371 DecoderFactory::~DecoderFactory() { }
373 void RegisterDecoder(StringView name
, UniquePtr
<DecoderFactory
> factory
)
375 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
376 [](const DecoderEntryPair
&entry
, StringView rhs
) -> bool
377 { return entry
.first
< rhs
; }
379 if(iter
!= sDecoders
.end())
380 throw std::runtime_error("Decoder factory already registered");
381 sDecoders
.insert(iter
, std::make_pair(String(name
), std::move(factory
)));
384 UniquePtr
<DecoderFactory
> UnregisterDecoder(StringView name
) noexcept
386 UniquePtr
<DecoderFactory
> factory
;
387 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
388 [](const DecoderEntryPair
&entry
, StringView rhs
) noexcept
-> bool
389 { return entry
.first
< rhs
; }
391 if(iter
!= sDecoders
.end())
393 factory
= std::move(iter
->second
);
394 sDecoders
.erase(iter
);
401 FileIOFactory::~FileIOFactory() { }
403 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
) noexcept
405 sFileFactory
.swap(factory
);
409 FileIOFactory
&FileIOFactory::get() noexcept
411 FileIOFactory
*factory
= sFileFactory
.get();
412 if(factory
) return *factory
;
413 return sDefaultFileFactory
;
417 // Default message handler methods are no-ops.
418 MessageHandler::~MessageHandler()
422 void MessageHandler::deviceDisconnected(Device
) noexcept
426 void MessageHandler::sourceStopped(Source
) noexcept
430 void MessageHandler::sourceForceStopped(Source
) noexcept
434 void MessageHandler::bufferLoading(StringView
, ChannelConfig
, SampleType
, ALuint
, ArrayView
<ALbyte
>) noexcept
438 String
MessageHandler::resourceNotFound(StringView
) noexcept
445 static inline void LoadALFunc(T
**func
, const char *name
)
446 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
448 static void LoadNothing(ContextImpl
*) { }
450 static void LoadEFX(ContextImpl
*ctx
)
452 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
453 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
454 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
455 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
456 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
457 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
458 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
459 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
460 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
461 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
462 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
464 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
465 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
466 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
467 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
468 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
469 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
470 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
471 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
472 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
473 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
474 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
476 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
477 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
478 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
479 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
480 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
481 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
482 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
483 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
484 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
485 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
486 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
489 static void LoadSourceResampler(ContextImpl
*ctx
)
491 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
494 static void LoadSourceLatency(ContextImpl
*ctx
)
496 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
497 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
500 static const struct {
503 void (&loader
)(ContextImpl
*);
504 } ALExtensionList
[] = {
505 { AL::EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
507 { AL::EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
508 { AL::EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
509 { AL::EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
511 { AL::EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
512 { AL::EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
513 { AL::EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
515 { AL::SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
516 { AL::SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
517 { AL::SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
518 { AL::SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
520 { AL::EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
522 { AL::EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
523 { AL::EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
527 ContextImpl
*ContextImpl::sCurrentCtx
= nullptr;
528 thread_local ContextImpl
*ContextImpl::sThreadCurrentCtx
= nullptr;
530 std::atomic
<uint64_t> ContextImpl::sContextSetCount
{0};
532 void ContextImpl::MakeCurrent(ContextImpl
*context
)
534 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
536 if(alcMakeContextCurrent(context
? context
->getALCcontext() : nullptr) == ALC_FALSE
)
537 throw std::runtime_error("Call to alcMakeContextCurrent failed");
541 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
543 std::swap(sCurrentCtx
, context
);
544 if(context
) context
->decRef();
546 if(sThreadCurrentCtx
)
547 sThreadCurrentCtx
->decRef();
548 sThreadCurrentCtx
= nullptr;
549 sContextSetCount
.fetch_add(1, std::memory_order_release
);
551 if((context
= sCurrentCtx
) != nullptr)
554 context
->mWakeThread
.notify_all();
558 void ContextImpl::MakeThreadCurrent(ContextImpl
*context
)
560 if(!DeviceManagerImpl::SetThreadContext
)
561 throw std::runtime_error("Thread-local contexts unsupported");
562 if(DeviceManagerImpl::SetThreadContext(context
? context
->getALCcontext() : nullptr) == ALC_FALSE
)
563 throw std::runtime_error("Call to alcSetThreadContext failed");
567 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
569 if(sThreadCurrentCtx
)
570 sThreadCurrentCtx
->decRef();
571 sThreadCurrentCtx
= context
;
572 sContextSetCount
.fetch_add(1, std::memory_order_release
);
575 void ContextImpl::setupExts()
577 ALCdevice
*device
= mDevice
->getALCdevice();
579 for(const auto &entry
: ALExtensionList
)
581 if((strncmp(entry
.name
, "ALC", 3) == 0) ? alcIsExtensionPresent(device
, entry
.name
) :
582 alIsExtensionPresent(entry
.name
))
584 mHasExt
.set(static_cast<size_t>(entry
.extension
));
591 void ContextImpl::backgroundProc()
593 if(DeviceManagerImpl::SetThreadContext
&& mDevice
->hasExtension(ALC::EXT_thread_local_context
))
594 DeviceManagerImpl::SetThreadContext(getALCcontext());
596 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
597 std::chrono::milliseconds
waketime(0);
598 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
599 while(!mQuitThread
.load(std::memory_order_acquire
))
602 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
603 mStreamingSources
.erase(
604 std::remove_if(mStreamingSources
.begin(), mStreamingSources
.end(),
605 [](SourceImpl
*source
) -> bool
606 { return !source
->updateAsync(); }
607 ), mStreamingSources
.end()
611 // Only do one pending buffer at a time. In case there's several large
612 // buffers to load, we still need to process streaming sources so they
614 PendingPromise
*lastpb
= mPendingCurrent
.load(std::memory_order_acquire
);
615 if(PendingPromise
*pb
= lastpb
->mNext
.load(std::memory_order_relaxed
))
617 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, std::move(pb
->mDecoder
), this);
618 pb
->mPromise
.set_value(Buffer(pb
->mBuffer
));
619 Promise
<Buffer
>().swap(pb
->mPromise
);
620 mPendingCurrent
.store(pb
, std::memory_order_release
);
624 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
625 if(!mQuitThread
.load(std::memory_order_acquire
) && lastpb
->mNext
.load(std::memory_order_acquire
) == nullptr)
629 std::chrono::milliseconds interval
= mWakeInterval
.load(std::memory_order_relaxed
);
630 if(interval
.count() == 0)
631 mWakeThread
.wait(wakelock
);
634 auto now
= std::chrono::steady_clock::now() - basetime
;
637 auto mult
= (now
-waketime
+ interval
-std::chrono::milliseconds(1)) / interval
;
638 waketime
+= interval
* mult
;
640 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
645 while(!mQuitThread
.load(std::memory_order_acquire
) &&
646 alcGetCurrentContext() != getALCcontext())
647 mWakeThread
.wait(ctxlock
);
652 if(DeviceManagerImpl::SetThreadContext
)
653 DeviceManagerImpl::SetThreadContext(nullptr);
657 ContextImpl::ContextImpl(ALCcontext
*context
, DeviceImpl
*device
)
658 : mContextSetCounter(std::numeric_limits
<uint64_t>::max()),
659 mListener(this), mContext(context
), mDevice(device
),
660 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
661 mRefs(0), mIsConnected(true), mIsBatching(false),
662 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
663 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
664 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
665 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
666 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
667 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
668 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
669 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
670 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
671 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
674 mSourceIds
.reserve(256);
675 mPendingHead
= new PendingPromise
;
676 mPendingCurrent
.store(mPendingHead
, std::memory_order_relaxed
);
677 mPendingTail
= mPendingHead
;
680 ContextImpl::~ContextImpl()
682 PendingPromise
*pb
= mPendingTail
;
685 PendingPromise
*next
= pb
->mNext
.load(std::memory_order_relaxed
);
689 mPendingCurrent
.store(nullptr, std::memory_order_relaxed
);
690 mPendingHead
= nullptr;
691 mPendingTail
= nullptr;
695 void Context::destroy()
697 ContextImpl
*i
= pImpl
;
701 void ContextImpl::destroy()
704 throw std::runtime_error("Context is in use");
705 if(!mBuffers
.empty())
706 throw std::runtime_error("Trying to destroy a context with buffers");
708 if(mThread
.joinable())
710 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
711 mQuitThread
.store(true, std::memory_order_release
);
713 mWakeThread
.notify_all();
717 alcDestroyContext(mContext
);
720 mDevice
->removeContext(this);
724 DECL_THUNK0(void, Context
, startBatch
,)
725 void ContextImpl::startBatch()
727 alcSuspendContext(mContext
);
731 DECL_THUNK0(void, Context
, endBatch
,)
732 void ContextImpl::endBatch()
734 alcProcessContext(mContext
);
739 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
740 SharedPtr
<MessageHandler
> ContextImpl::setMessageHandler(SharedPtr
<MessageHandler
>&& handler
)
742 std::lock_guard
<std::mutex
> lock(mGlobalCtxMutex
);
743 mMessage
.swap(handler
);
748 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
749 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval
)
751 if(interval
.count() < 0 || interval
> std::chrono::seconds(1))
752 throw std::out_of_range("Async wake interval out of range");
753 mWakeInterval
.store(interval
);
754 mWakeMutex
.lock(); mWakeMutex
.unlock();
755 mWakeThread
.notify_all();
759 DecoderOrExceptT
ContextImpl::findDecoder(StringView name
)
761 DecoderOrExceptT ret
;
763 String oldname
= String(name
);
764 auto file
= FileIOFactory::get().openFile(oldname
);
765 if(file
) return (ret
= GetDecoder(std::move(file
)));
767 // Resource not found. Try to find a substitute.
769 return (ret
= std::make_exception_ptr(std::runtime_error("Failed to open file")));
771 String
newname(mMessage
->resourceNotFound(oldname
));
773 return (ret
= std::make_exception_ptr(std::runtime_error("Failed to open file")));
774 file
= FileIOFactory::get().openFile(newname
);
775 oldname
= std::move(newname
);
778 return (ret
= GetDecoder(std::move(file
)));
781 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, StringView
)
782 SharedPtr
<Decoder
> ContextImpl::createDecoder(StringView name
)
785 DecoderOrExceptT dec
= findDecoder(name
);
786 if(SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
))
788 std::rethrow_exception(std::get
<std::exception_ptr
>(dec
));
792 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
793 bool ContextImpl::isSupported(ChannelConfig channels
, SampleType type
) const
796 return GetFormat(channels
, type
) != AL_NONE
;
800 DECL_THUNK0(ArrayView
<String
>, Context
, getAvailableResamplers
,)
801 ArrayView
<String
> ContextImpl::getAvailableResamplers()
804 if(mResamplers
.empty() && hasExtension(AL::SOFT_source_resampler
))
806 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
807 mResamplers
.reserve(num_resamplers
);
808 for(int i
= 0;i
< num_resamplers
;i
++)
809 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
810 if(mResamplers
.empty())
811 mResamplers
.emplace_back();
816 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
817 ALsizei
ContextImpl::getDefaultResamplerIndex() const
820 if(!hasExtension(AL::SOFT_source_resampler
))
822 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
826 BufferOrExceptT
ContextImpl::doCreateBuffer(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
828 BufferOrExceptT retval
;
829 ALuint srate
= decoder
->getFrequency();
830 ChannelConfig chans
= decoder
->getChannelConfig();
831 SampleType type
= decoder
->getSampleType();
832 ALuint frames
= decoder
->getLength();
834 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
835 frames
= decoder
->read(data
.data(), frames
);
837 return (retval
= std::make_exception_ptr(std::runtime_error("No samples for buffer")));
838 data
.resize(FramesToBytes(frames
, chans
, type
));
840 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
841 if(loop_pts
.first
>= loop_pts
.second
)
842 loop_pts
= std::make_pair(0, frames
);
845 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
846 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
849 // Get the format before calling the bufferLoading message handler, to
850 // ensure it's something OpenAL can handle.
851 ALenum format
= GetFormat(chans
, type
);
852 if(format
== AL_NONE
)
854 String
str("Unsupported format (");
855 str
+= GetSampleTypeName(type
);
857 str
+= GetChannelConfigName(chans
);
859 return (retval
= std::make_exception_ptr(std::runtime_error(str
)));
863 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
867 alGenBuffers(1, &bid
);
868 alBufferData(bid
, format
, data
.data(), data
.size(), srate
);
869 if(hasExtension(AL::SOFT_loop_points
))
871 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
872 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
874 ALenum err
= alGetError();
875 if(err
!= AL_NO_ERROR
)
877 alDeleteBuffers(1, &bid
);
878 return (retval
= std::make_exception_ptr(al_error(err
, "Failed to buffer data")));
881 return (retval
= mBuffers
.insert(iter
,
882 MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
)
886 BufferOrExceptT
ContextImpl::doCreateBufferAsync(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
, Promise
<Buffer
> promise
)
888 BufferOrExceptT retval
;
889 ALuint srate
= decoder
->getFrequency();
890 ChannelConfig chans
= decoder
->getChannelConfig();
891 SampleType type
= decoder
->getSampleType();
892 ALuint frames
= decoder
->getLength();
894 return (retval
= std::make_exception_ptr(std::runtime_error("No samples for buffer")));
896 ALenum format
= GetFormat(chans
, type
);
897 if(format
== AL_NONE
)
899 std::stringstream sstr
;
900 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
901 return (retval
= std::make_exception_ptr(std::runtime_error(sstr
.str())));
906 alGenBuffers(1, &bid
);
907 ALenum err
= alGetError();
908 if(err
!= AL_NO_ERROR
)
909 return (retval
= std::make_exception_ptr(al_error(err
, "Failed to create buffer")));
911 auto buffer
= MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
);
913 if(mThread
.get_id() == std::thread::id())
914 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
916 PendingPromise
*pf
= nullptr;
917 if(mPendingTail
== mPendingCurrent
.load(std::memory_order_acquire
))
918 pf
= new PendingPromise
{buffer
.get(), decoder
, format
, frames
, std::move(promise
)};
922 pf
->mBuffer
= buffer
.get();
923 pf
->mDecoder
= decoder
;
924 pf
->mFormat
= format
;
925 pf
->mFrames
= frames
;
926 pf
->mPromise
= std::move(promise
);
927 mPendingTail
= pf
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
930 mPendingHead
->mNext
.store(pf
, std::memory_order_release
);
933 return (retval
= mBuffers
.insert(iter
, std::move(buffer
))->get());
936 DECL_THUNK1(Buffer
, Context
, getBuffer
,, StringView
)
937 Buffer
ContextImpl::getBuffer(StringView name
)
941 auto hasher
= std::hash
<StringView
>();
942 if(UNLIKELY(!mFutureBuffers
.empty()))
946 // If the buffer is already pending for the future, wait for it
947 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
948 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
949 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
951 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
953 buffer
= iter
->mFuture
.get();
954 mFutureBuffers
.erase(iter
);
957 // Clear out any completed futures.
958 mFutureBuffers
.erase(
959 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
960 [](const PendingBuffer
&entry
) -> bool
961 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
962 ), mFutureBuffers
.end()
965 // If we got the buffer, return it. Otherwise, go load it normally.
966 if(buffer
) return buffer
;
969 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
970 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
971 { return hasher(lhs
->getName()) < rhs
; }
973 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
974 return Buffer(iter
->get());
976 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, createDecoder(name
));
977 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
978 if(UNLIKELY(!buffer
))
979 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
983 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, getBufferAsync
,, StringView
)
984 SharedFuture
<Buffer
> ContextImpl::getBufferAsync(StringView name
)
986 SharedFuture
<Buffer
> future
;
989 auto hasher
= std::hash
<StringView
>();
990 if(UNLIKELY(!mFutureBuffers
.empty()))
992 // Check if the future that's being created already exists
993 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
994 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
995 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
997 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
999 future
= iter
->mFuture
;
1000 if(GetFutureState(future
) == std::future_status::ready
)
1001 mFutureBuffers
.erase(iter
);
1005 // Clear out any fulfilled futures.
1006 mFutureBuffers
.erase(
1007 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1008 [](const PendingBuffer
&entry
) -> bool
1009 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1010 ), mFutureBuffers
.end()
1014 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1015 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1016 { return hasher(lhs
->getName()) < rhs
; }
1018 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1020 // User asked to create a future buffer that's already loaded. Just
1021 // construct a promise, fulfill the promise immediately, then return a
1022 // shared future that's already set.
1023 Promise
<Buffer
> promise
;
1024 promise
.set_value(Buffer(iter
->get()));
1025 future
= promise
.get_future().share();
1029 Promise
<Buffer
> promise
;
1030 future
= promise
.get_future().share();
1032 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, createDecoder(name
), std::move(promise
));
1033 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1034 if(UNLIKELY(!buffer
))
1035 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1036 mWakeMutex
.lock(); mWakeMutex
.unlock();
1037 mWakeThread
.notify_all();
1039 mFutureBuffers
.insert(
1040 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1041 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1042 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1043 ), { buffer
->getHandle(), future
}
1049 DECL_THUNK1(void, Context
, precacheBuffersAsync
,, ArrayView
<StringView
>)
1050 void ContextImpl::precacheBuffersAsync(ArrayView
<StringView
> names
)
1054 if(UNLIKELY(!mFutureBuffers
.empty()))
1056 // Clear out any fulfilled futures.
1057 mFutureBuffers
.erase(
1058 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1059 [](const PendingBuffer
&entry
) -> bool
1060 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1061 ), mFutureBuffers
.end()
1065 auto hasher
= std::hash
<StringView
>();
1066 for(const StringView name
: names
)
1068 // Check if the buffer that's being created already exists
1069 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1070 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1071 { return hasher(lhs
->getName()) < rhs
; }
1073 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1076 DecoderOrExceptT dec
= findDecoder(name
);
1077 SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
);
1078 if(!decoder
) continue;
1080 Promise
<Buffer
> promise
;
1081 SharedFuture
<Buffer
> future
= promise
.get_future().share();
1083 BufferOrExceptT buf
= doCreateBufferAsync(name
, iter
, std::move(*decoder
),
1084 std::move(promise
));
1085 Buffer
*buffer
= std::get_if
<Buffer
>(&buf
);
1086 if(UNLIKELY(!buffer
)) continue;
1088 mFutureBuffers
.insert(
1089 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1090 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1091 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1092 ), { buffer
->getHandle(), future
}
1095 mWakeMutex
.lock(); mWakeMutex
.unlock();
1096 mWakeThread
.notify_all();
1099 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, StringView
, SharedPtr
<Decoder
>)
1100 Buffer
ContextImpl::createBufferFrom(StringView name
, SharedPtr
<Decoder
>&& decoder
)
1104 auto hasher
= std::hash
<StringView
>();
1105 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1106 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1107 { return hasher(lhs
->getName()) < rhs
; }
1109 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1110 throw std::runtime_error("Buffer already exists");
1112 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, std::move(decoder
));
1113 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1114 if(UNLIKELY(!buffer
))
1115 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1119 DECL_THUNK2(SharedFuture
<Buffer
>, Context
, createBufferAsyncFrom
,, StringView
, SharedPtr
<Decoder
>)
1120 SharedFuture
<Buffer
> ContextImpl::createBufferAsyncFrom(StringView name
, SharedPtr
<Decoder
>&& decoder
)
1122 SharedFuture
<Buffer
> future
;
1125 if(UNLIKELY(!mFutureBuffers
.empty()))
1127 // Clear out any fulfilled futures.
1128 mFutureBuffers
.erase(
1129 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1130 [](const PendingBuffer
&entry
) -> bool
1131 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1132 ), mFutureBuffers
.end()
1136 auto hasher
= std::hash
<StringView
>();
1137 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1138 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1139 { return hasher(lhs
->getName()) < rhs
; }
1141 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1142 throw std::runtime_error("Buffer already exists");
1144 Promise
<Buffer
> promise
;
1145 future
= promise
.get_future().share();
1147 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, std::move(decoder
), std::move(promise
));
1148 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1149 if(UNLIKELY(!buffer
))
1150 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1151 mWakeMutex
.lock(); mWakeMutex
.unlock();
1152 mWakeThread
.notify_all();
1154 mFutureBuffers
.insert(
1155 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1156 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1157 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1158 ), { buffer
->getHandle(), future
}
1165 DECL_THUNK1(Buffer
, Context
, findBuffer
,, StringView
)
1166 Buffer
ContextImpl::findBuffer(StringView name
)
1171 auto hasher
= std::hash
<StringView
>();
1172 if(UNLIKELY(!mFutureBuffers
.empty()))
1174 // If the buffer is already pending for the future, wait for it
1175 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1176 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1177 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1179 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1181 buffer
= iter
->mFuture
.get();
1182 mFutureBuffers
.erase(iter
);
1185 // Clear out any completed futures.
1186 mFutureBuffers
.erase(
1187 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1188 [](const PendingBuffer
&entry
) -> bool
1189 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1190 ), mFutureBuffers
.end()
1196 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1197 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1198 { return hasher(lhs
->getName()) < rhs
; }
1200 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1201 buffer
= Buffer(iter
->get());
1206 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, findBufferAsync
,, StringView
)
1207 SharedFuture
<Buffer
> ContextImpl::findBufferAsync(StringView name
)
1209 SharedFuture
<Buffer
> future
;
1212 auto hasher
= std::hash
<StringView
>();
1213 if(UNLIKELY(!mFutureBuffers
.empty()))
1215 // Check if the future that's being created already exists
1216 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1217 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1218 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1220 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1222 future
= iter
->mFuture
;
1223 if(GetFutureState(future
) == std::future_status::ready
)
1224 mFutureBuffers
.erase(iter
);
1228 // Clear out any fulfilled futures.
1229 mFutureBuffers
.erase(
1230 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1231 [](const PendingBuffer
&entry
) -> bool
1232 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1233 ), mFutureBuffers
.end()
1237 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1238 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1239 { return hasher(lhs
->getName()) < rhs
; }
1241 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1243 // User asked to create a future buffer that's already loaded. Just
1244 // construct a promise, fulfill the promise immediately, then return a
1245 // shared future that's already set.
1246 Promise
<Buffer
> promise
;
1247 promise
.set_value(Buffer(iter
->get()));
1248 future
= promise
.get_future().share();
1254 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1255 DECL_THUNK1(void, Context
, removeBuffer
,, StringView
)
1256 void ContextImpl::removeBuffer(StringView name
)
1260 auto hasher
= std::hash
<StringView
>();
1261 if(UNLIKELY(!mFutureBuffers
.empty()))
1263 // If the buffer is already pending for the future, wait for it to
1264 // finish before continuing.
1265 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1266 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1267 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1269 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1271 iter
->mFuture
.wait();
1272 mFutureBuffers
.erase(iter
);
1275 // Clear out any completed futures.
1276 mFutureBuffers
.erase(
1277 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1278 [](const PendingBuffer
&entry
) -> bool
1279 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1280 ), mFutureBuffers
.end()
1284 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1285 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1286 { return hasher(lhs
->getName()) < rhs
; }
1288 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1290 // Remove pending sources whose future was waiting for this buffer.
1291 mPendingSources
.erase(
1292 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1293 [iter
](PendingSource
&entry
) -> bool
1295 return (GetFutureState(entry
.mFuture
) == std::future_status::ready
&&
1296 entry
.mFuture
.get().getHandle() == iter
->get());
1298 ), mPendingSources
.end()
1301 mBuffers
.erase(iter
);
1306 ALuint
ContextImpl::getSourceId(ALuint maxprio
)
1309 if(mSourceIds
.empty())
1312 alGenSources(1, &id
);
1313 if(alGetError() == AL_NO_ERROR
)
1316 SourceImpl
*lowest
= nullptr;
1317 for(SourceBufferUpdateEntry
&entry
: mPlaySources
)
1319 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1320 lowest
= entry
.mSource
;
1322 for(SourceStreamUpdateEntry
&entry
: mStreamSources
)
1324 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1325 lowest
= entry
.mSource
;
1327 if(lowest
&& lowest
->getPriority() < maxprio
)
1331 mMessage
->sourceForceStopped(lowest
);
1334 if(mSourceIds
.empty())
1335 throw std::runtime_error("No available sources");
1337 id
= mSourceIds
.back();
1338 mSourceIds
.pop_back();
1343 DECL_THUNK0(Source
, Context
, createSource
,)
1344 Source
ContextImpl::createSource()
1349 if(!mFreeSources
.empty())
1351 source
= mFreeSources
.back();
1352 mFreeSources
.pop_back();
1356 mAllSources
.emplace_back(this);
1357 source
= &mAllSources
.back();
1359 return Source(source
);
1363 void ContextImpl::addPendingSource(SourceImpl
*source
, SharedFuture
<Buffer
> future
)
1365 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1366 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1367 { return lhs
.mSource
< rhs
; }
1369 if(iter
== mPendingSources
.end() || iter
->mSource
!= source
)
1370 mPendingSources
.insert(iter
, {source
, std::move(future
)});
1373 void ContextImpl::removePendingSource(SourceImpl
*source
)
1375 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1376 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1377 { return lhs
.mSource
< rhs
; }
1379 if(iter
!= mPendingSources
.end() && iter
->mSource
== source
)
1380 mPendingSources
.erase(iter
);
1383 bool ContextImpl::isPendingSource(const SourceImpl
*source
) const
1385 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1386 [](const PendingSource
&lhs
, const SourceImpl
*rhs
) -> bool
1387 { return lhs
.mSource
< rhs
; }
1389 return (iter
!= mPendingSources
.end() && iter
->mSource
== source
);
1392 void ContextImpl::addFadingSource(SourceImpl
*source
)
1394 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1395 [](SourceImpl
*lhs
, SourceImpl
*rhs
) -> bool
1396 { return lhs
< rhs
; }
1398 if(iter
== mFadingSources
.end() || *iter
!= source
)
1399 mFadingSources
.insert(iter
, source
);
1402 void ContextImpl::removeFadingSource(SourceImpl
*source
)
1404 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1405 [](SourceImpl
*lhs
, SourceImpl
*rhs
) -> bool
1406 { return lhs
< rhs
; }
1408 if(iter
!= mFadingSources
.end() && *iter
== source
)
1409 mFadingSources
.erase(iter
);
1412 void ContextImpl::addPlayingSource(SourceImpl
*source
, ALuint id
)
1414 auto iter
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1415 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1416 { return lhs
.mSource
< rhs
; }
1418 if(iter
== mPlaySources
.end() || iter
->mSource
!= source
)
1419 mPlaySources
.insert(iter
, {source
,id
});
1422 void ContextImpl::addPlayingSource(SourceImpl
*source
)
1424 auto iter
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1425 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1426 { return lhs
.mSource
< rhs
; }
1428 if(iter
== mStreamSources
.end() || iter
->mSource
!= source
)
1429 mStreamSources
.insert(iter
, {source
});
1432 void ContextImpl::removePlayingSource(SourceImpl
*source
)
1434 auto iter0
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1435 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1436 { return lhs
.mSource
< rhs
; }
1438 if(iter0
!= mPlaySources
.end() && iter0
->mSource
== source
)
1439 mPlaySources
.erase(iter0
);
1442 auto iter1
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1443 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1444 { return lhs
.mSource
< rhs
; }
1446 if(iter1
!= mStreamSources
.end() && iter1
->mSource
== source
)
1447 mStreamSources
.erase(iter1
);
1452 void ContextImpl::addStream(SourceImpl
*source
)
1454 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1455 if(mThread
.get_id() == std::thread::id())
1456 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
1457 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1458 if(iter
== mStreamingSources
.end() || *iter
!= source
)
1459 mStreamingSources
.insert(iter
, source
);
1462 void ContextImpl::removeStream(SourceImpl
*source
)
1464 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1465 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1466 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1467 mStreamingSources
.erase(iter
);
1470 void ContextImpl::removeStreamNoLock(SourceImpl
*source
)
1472 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1473 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1474 mStreamingSources
.erase(iter
);
1478 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1479 AuxiliaryEffectSlot
ContextImpl::createAuxiliaryEffectSlot()
1481 if(!hasExtension(AL::EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
1482 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1487 alGenAuxiliaryEffectSlots(1, &id
);
1488 ALenum err
= alGetError();
1489 if(err
!= AL_NO_ERROR
)
1490 throw al_error(err
, "Failed to create AuxiliaryEffectSlot");
1492 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id
));
1495 alDeleteAuxiliaryEffectSlots(1, &id
);
1501 DECL_THUNK0(Effect
, Context
, createEffect
,)
1502 Effect
ContextImpl::createEffect()
1504 if(!hasExtension(AL::EXT_EFX
))
1505 throw std::runtime_error("Effects not supported");
1510 alGenEffects(1, &id
);
1511 ALenum err
= alGetError();
1512 if(err
!= AL_NO_ERROR
)
1513 throw al_error(err
, "Failed to create Effect");
1515 return Effect(new EffectImpl(this, id
));
1518 alDeleteEffects(1, &id
);
1524 DECL_THUNK0(SourceGroup
, Context
, createSourceGroup
,)
1525 SourceGroup
ContextImpl::createSourceGroup()
1527 auto srcgroup
= MakeUnique
<SourceGroupImpl
>(this);
1528 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), srcgroup
);
1530 iter
= mSourceGroups
.insert(iter
, std::move(srcgroup
));
1531 return SourceGroup(iter
->get());
1534 void ContextImpl::freeSourceGroup(SourceGroupImpl
*group
)
1536 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), group
,
1537 [](const UniquePtr
<SourceGroupImpl
> &lhs
, SourceGroupImpl
*rhs
) -> bool
1538 { return lhs
.get() < rhs
; }
1540 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1541 mSourceGroups
.erase(iter
);
1545 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1546 void ContextImpl::setDopplerFactor(ALfloat factor
)
1548 if(!(factor
>= 0.0f
))
1549 throw std::out_of_range("Doppler factor out of range");
1551 alDopplerFactor(factor
);
1555 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1556 void ContextImpl::setSpeedOfSound(ALfloat speed
)
1559 throw std::out_of_range("Speed of sound out of range");
1561 alSpeedOfSound(speed
);
1565 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1566 void ContextImpl::setDistanceModel(DistanceModel model
)
1569 alDistanceModel((ALenum
)model
);
1573 DECL_THUNK0(void, Context
, update
,)
1574 void ContextImpl::update()
1577 mPendingSources
.erase(
1578 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1579 [](PendingSource
&entry
) -> bool
1580 { return !entry
.mSource
->checkPending(entry
.mFuture
); }
1581 ), mPendingSources
.end()
1583 if(!mFadingSources
.empty())
1585 auto cur_time
= std::chrono::steady_clock::now().time_since_epoch();
1586 mFadingSources
.erase(
1587 std::remove_if(mFadingSources
.begin(), mFadingSources
.end(),
1588 [cur_time
](SourceImpl
*source
) -> bool
1589 { return !source
->fadeUpdate(cur_time
); }
1590 ), mFadingSources
.end()
1594 std::remove_if(mPlaySources
.begin(), mPlaySources
.end(),
1595 [](const SourceBufferUpdateEntry
&entry
) -> bool
1596 { return !entry
.mSource
->playUpdate(entry
.mId
); }
1597 ), mPlaySources
.end()
1599 mStreamSources
.erase(
1600 std::remove_if(mStreamSources
.begin(), mStreamSources
.end(),
1601 [](const SourceStreamUpdateEntry
&entry
) -> bool
1602 { return !entry
.mSource
->playUpdate(); }
1603 ), mStreamSources
.end()
1606 if(!mWakeInterval
.load(std::memory_order_relaxed
).count())
1608 // For performance reasons, don't wait for the thread's mutex. This
1609 // should be called often enough to keep up with any and all streams
1611 mWakeThread
.notify_all();
1614 if(hasExtension(AL::EXT_disconnect
) && mIsConnected
)
1617 alcGetIntegerv(mDevice
->getALCdevice(), ALC_CONNECTED
, 1, &connected
);
1618 mIsConnected
= connected
;
1619 if(!connected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(mDevice
));
1623 DECL_THUNK0(Device
, Context
, getDevice
,)
1624 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1625 DECL_THUNK0(Listener
, Context
, getListener
,)
1626 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1628 void Context::MakeCurrent(Context context
)
1629 { ContextImpl::MakeCurrent(context
.pImpl
); }
1631 Context
Context::GetCurrent()
1632 { return Context(ContextImpl::GetCurrent()); }
1634 void Context::MakeThreadCurrent(Context context
)
1635 { ContextImpl::MakeThreadCurrent(context
.pImpl
); }
1637 Context
Context::GetThreadCurrent()
1638 { return Context(ContextImpl::GetThreadCurrent()); }
1641 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1642 void ListenerImpl::setGain(ALfloat gain
)
1645 throw std::out_of_range("Gain out of range");
1646 CheckContext(mContext
);
1647 alListenerf(AL_GAIN
, gain
);
1651 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, const Vector3Pair
&)
1652 void ListenerImpl::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, const std::pair
<Vector3
,Vector3
> &orientation
)
1654 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1655 CheckContext(mContext
);
1656 Batcher batcher
= mContext
->getBatcher();
1657 alListenerfv(AL_POSITION
, position
.getPtr());
1658 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1659 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1662 DECL_THUNK1(void, Listener
, setPosition
,, const Vector3
&)
1663 void ListenerImpl::setPosition(const Vector3
&position
)
1665 CheckContext(mContext
);
1666 alListenerfv(AL_POSITION
, position
.getPtr());
1669 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1670 void ListenerImpl::setPosition(const ALfloat
*pos
)
1672 CheckContext(mContext
);
1673 alListenerfv(AL_POSITION
, pos
);
1676 DECL_THUNK1(void, Listener
, setVelocity
,, const Vector3
&)
1677 void ListenerImpl::setVelocity(const Vector3
&velocity
)
1679 CheckContext(mContext
);
1680 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1683 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1684 void ListenerImpl::setVelocity(const ALfloat
*vel
)
1686 CheckContext(mContext
);
1687 alListenerfv(AL_VELOCITY
, vel
);
1690 DECL_THUNK1(void, Listener
, setOrientation
,, const Vector3Pair
&)
1691 void ListenerImpl::setOrientation(const std::pair
<Vector3
,Vector3
> &orientation
)
1693 CheckContext(mContext
);
1694 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1697 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1698 void ListenerImpl::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1700 CheckContext(mContext
);
1701 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1702 alListenerfv(AL_ORIENTATION
, ori
);
1705 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1706 void ListenerImpl::setOrientation(const ALfloat
*ori
)
1708 CheckContext(mContext
);
1709 alListenerfv(AL_ORIENTATION
, ori
);
1712 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)
1713 void ListenerImpl::setMetersPerUnit(ALfloat m_u
)
1716 throw std::out_of_range("Invalid meters per unit");
1717 CheckContext(mContext
);
1718 if(mContext
->hasExtension(AL::EXT_EFX
))
1719 alListenerf(AL_METERS_PER_UNIT
, m_u
);