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::StringView name
, alure::UniquePtr
<std::istream
> &file
,
257 alure::DecoderOrExceptT ret
;
260 alure::DecoderFactory
*factory
= start
->second
.get();
261 auto decoder
= factory
->createDecoder(file
);
262 if(decoder
) return (ret
= std::move(decoder
));
264 if(!file
|| !(file
->clear(),file
->seekg(0)))
265 return (ret
= std::runtime_error("Failed to rewind "+name
+" for the next decoder factory"));
270 return (ret
= nullptr);
273 static alure::DecoderOrExceptT
GetDecoder(alure::StringView name
, alure::UniquePtr
<std::istream
> file
)
275 auto decoder
= GetDecoder(name
, file
, sDecoders
.begin(), sDecoders
.end());
276 if(std::holds_alternative
<std::runtime_error
>(decoder
)) return decoder
;
277 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
278 decoder
= GetDecoder(name
, file
, std::begin(sDefaultDecoders
), std::end(sDefaultDecoders
));
279 if(std::holds_alternative
<std::runtime_error
>(decoder
)) return decoder
;
280 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
281 return (decoder
= std::runtime_error("No decoder for "+name
));
284 class DefaultFileIOFactory final
: public alure::FileIOFactory
{
285 alure::UniquePtr
<std::istream
> openFile(const alure::String
&name
) 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 Decoder::~Decoder() { }
306 DecoderFactory::~DecoderFactory() { }
308 void RegisterDecoder(StringView name
, UniquePtr
<DecoderFactory
> factory
)
310 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
311 [](const DecoderEntryPair
&entry
, StringView rhs
) -> bool
312 { return entry
.first
< rhs
; }
314 if(iter
!= sDecoders
.end())
315 throw std::runtime_error("Decoder factory \""+name
+"\" already registered");
316 sDecoders
.insert(iter
, std::make_pair(String(name
), std::move(factory
)));
319 UniquePtr
<DecoderFactory
> UnregisterDecoder(StringView name
)
321 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
322 [](const DecoderEntryPair
&entry
, StringView rhs
) -> bool
323 { return entry
.first
< rhs
; }
325 if(iter
!= sDecoders
.end())
327 UniquePtr
<DecoderFactory
> factory
= std::move(iter
->second
);
328 sDecoders
.erase(iter
);
335 FileIOFactory::~FileIOFactory() { }
337 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
)
339 std::swap(sFileFactory
, factory
);
343 FileIOFactory
&FileIOFactory::get()
345 FileIOFactory
*factory
= sFileFactory
.get();
346 if(factory
) return *factory
;
347 return sDefaultFileFactory
;
351 // Default message handler methods are no-ops.
352 MessageHandler::~MessageHandler()
356 void MessageHandler::deviceDisconnected(Device
)
360 void MessageHandler::sourceStopped(Source
)
364 void MessageHandler::sourceForceStopped(Source
)
368 void MessageHandler::bufferLoading(StringView
, ChannelConfig
, SampleType
, ALuint
, ArrayView
<ALbyte
>)
372 String
MessageHandler::resourceNotFound(StringView
)
379 static inline void LoadALFunc(T
**func
, const char *name
)
380 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
382 static void LoadNothing(ContextImpl
*) { }
384 static void LoadEFX(ContextImpl
*ctx
)
386 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
387 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
388 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
389 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
390 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
391 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
392 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
393 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
394 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
395 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
396 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
398 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
399 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
400 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
401 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
402 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
403 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
404 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
405 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
406 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
407 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
408 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
410 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
411 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
412 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
413 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
414 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
415 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
416 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
417 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
418 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
419 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
420 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
423 static void LoadSourceResampler(ContextImpl
*ctx
)
425 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
428 static void LoadSourceLatency(ContextImpl
*ctx
)
430 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
431 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
434 static const struct {
435 enum ALExtension extension
;
437 void (&loader
)(ContextImpl
*);
438 } ALExtensionList
[] = {
439 { EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
441 { EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
442 { EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
443 { EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
445 { EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
446 { EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
447 { EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
449 { SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
450 { SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
451 { SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
452 { SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
454 { EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
456 { EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
457 { EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
461 ContextImpl
*ContextImpl::sCurrentCtx
= nullptr;
462 thread_local ContextImpl
*ContextImpl::sThreadCurrentCtx
= nullptr;
464 void ContextImpl::MakeCurrent(ContextImpl
*context
)
466 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
468 if(alcMakeContextCurrent(context
? context
->getContext() : nullptr) == ALC_FALSE
)
469 throw std::runtime_error("Call to alcMakeContextCurrent failed");
473 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
475 std::swap(sCurrentCtx
, context
);
476 if(context
) context
->decRef();
478 if(sThreadCurrentCtx
)
479 sThreadCurrentCtx
->decRef();
480 sThreadCurrentCtx
= 0;
482 if((context
= sCurrentCtx
) != nullptr)
485 context
->mWakeThread
.notify_all();
489 void ContextImpl::MakeThreadCurrent(ContextImpl
*context
)
491 if(!DeviceManagerImpl::SetThreadContext
)
492 throw std::runtime_error("Thread-local contexts unsupported");
493 if(DeviceManagerImpl::SetThreadContext(context
? context
->getContext() : nullptr) == ALC_FALSE
)
494 throw std::runtime_error("Call to alcSetThreadContext failed");
498 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
500 if(sThreadCurrentCtx
)
501 sThreadCurrentCtx
->decRef();
502 sThreadCurrentCtx
= context
;
505 void ContextImpl::setupExts()
507 ALCdevice
*device
= mDevice
->getDevice();
508 std::fill(std::begin(mHasExt
), std::end(mHasExt
), false);
509 for(const auto &entry
: ALExtensionList
)
511 mHasExt
[entry
.extension
] = (strncmp(entry
.name
, "ALC", 3) == 0) ?
512 alcIsExtensionPresent(device
, entry
.name
) :
513 alIsExtensionPresent(entry
.name
);
514 if(mHasExt
[entry
.extension
]) entry
.loader(this);
519 void ContextImpl::backgroundProc()
521 if(DeviceManagerImpl::SetThreadContext
&& mDevice
->hasExtension(EXT_thread_local_context
))
522 DeviceManagerImpl::SetThreadContext(getContext());
524 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
525 std::chrono::milliseconds
waketime(0);
526 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
527 while(!mQuitThread
.load(std::memory_order_acquire
))
530 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
531 mStreamingSources
.erase(
532 std::remove_if(mStreamingSources
.begin(), mStreamingSources
.end(),
533 [](SourceImpl
*source
) -> bool
534 { return !source
->updateAsync(); }
535 ), mStreamingSources
.end()
539 // Only do one pending buffer at a time. In case there's several large
540 // buffers to load, we still need to process streaming sources so they
542 PendingBuffer
*lastpb
= mPendingCurrent
.load(std::memory_order_acquire
);
543 if(PendingBuffer
*pb
= lastpb
->mNext
.load(std::memory_order_relaxed
))
545 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, std::move(pb
->mDecoder
), this);
546 pb
->mPromise
.set_value(Buffer(pb
->mBuffer
));
547 Promise
<Buffer
>().swap(pb
->mPromise
);
548 mPendingCurrent
.store(pb
, std::memory_order_release
);
552 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
553 if(!mQuitThread
.load(std::memory_order_acquire
) && lastpb
->mNext
.load(std::memory_order_acquire
) == nullptr)
557 std::chrono::milliseconds interval
= mWakeInterval
.load(std::memory_order_relaxed
);
558 if(interval
.count() == 0)
559 mWakeThread
.wait(wakelock
);
562 auto now
= std::chrono::steady_clock::now() - basetime
;
565 auto mult
= (now
-waketime
+ interval
-std::chrono::milliseconds(1)) / interval
;
566 waketime
+= interval
* mult
;
567 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
573 while(!mQuitThread
.load(std::memory_order_acquire
) &&
574 alcGetCurrentContext() != getContext())
575 mWakeThread
.wait(ctxlock
);
580 if(DeviceManagerImpl::SetThreadContext
)
581 DeviceManagerImpl::SetThreadContext(nullptr);
585 ContextImpl::ContextImpl(ALCcontext
*context
, DeviceImpl
*device
)
586 : mListener(this), mContext(context
), mDevice(device
),
587 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
588 mRefs(0), mHasExt
{false}, mIsConnected(true), mIsBatching(false),
589 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
590 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
591 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
592 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
593 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
594 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
595 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
596 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
597 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
598 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
600 mPendingHead
= new PendingBuffer
;
601 mPendingCurrent
.store(mPendingHead
, std::memory_order_relaxed
);
602 mPendingTail
= mPendingHead
;
605 ContextImpl::~ContextImpl()
607 PendingBuffer
*pb
= mPendingTail
;
610 PendingBuffer
*next
= pb
->mNext
.load(std::memory_order_relaxed
);
614 mPendingCurrent
.store(nullptr, std::memory_order_relaxed
);
615 mPendingHead
= nullptr;
616 mPendingTail
= nullptr;
620 void ContextImpl::destroy()
622 if(mRefs
.load() != 0)
623 throw std::runtime_error("Context is in use");
624 if(!mBuffers
.empty())
625 throw std::runtime_error("Trying to destroy a context with buffers");
627 if(mThread
.joinable())
629 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
630 mQuitThread
.store(true, std::memory_order_release
);
632 mWakeThread
.notify_all();
636 alcDestroyContext(mContext
);
639 mDevice
->removeContext(this);
643 void ContextImpl::startBatch()
645 alcSuspendContext(mContext
);
649 void ContextImpl::endBatch()
651 alcProcessContext(mContext
);
656 SharedPtr
<MessageHandler
> ContextImpl::setMessageHandler(SharedPtr
<MessageHandler
> handler
)
658 std::lock_guard
<std::mutex
> lock(mGlobalCtxMutex
);
659 mMessage
.swap(handler
);
664 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval
)
666 if(interval
.count() < 0 || interval
> std::chrono::seconds(1))
667 throw std::runtime_error("Async wake interval out of range");
668 mWakeInterval
.store(interval
);
669 mWakeMutex
.lock(); mWakeMutex
.unlock();
670 mWakeThread
.notify_all();
674 DecoderOrExceptT
ContextImpl::findDecoder(StringView name
)
676 DecoderOrExceptT ret
;
678 String oldname
= String(name
);
679 auto file
= FileIOFactory::get().openFile(oldname
);
680 if(file
) return (ret
= GetDecoder(name
, std::move(file
)));
682 // Resource not found. Try to find a substitute.
683 if(!mMessage
.get()) return (ret
= std::runtime_error("Failed to open "+oldname
));
685 String
newname(mMessage
->resourceNotFound(oldname
));
687 return (ret
= std::runtime_error("Failed to open "+oldname
));
688 file
= FileIOFactory::get().openFile(newname
);
689 oldname
= std::move(newname
);
692 return (ret
= GetDecoder(oldname
, std::move(file
)));
695 SharedPtr
<Decoder
> ContextImpl::createDecoder(StringView name
)
698 DecoderOrExceptT dec
= findDecoder(name
);
699 if(SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
))
701 throw std::get
<std::runtime_error
>(dec
);
705 bool ContextImpl::isSupported(ChannelConfig channels
, SampleType type
) const
708 return GetFormat(channels
, type
) != AL_NONE
;
712 ArrayView
<String
> ContextImpl::getAvailableResamplers()
715 if(mResamplers
.empty() && hasExtension(SOFT_source_resampler
))
717 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
718 mResamplers
.reserve(num_resamplers
);
719 for(int i
= 0;i
< num_resamplers
;i
++)
720 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
721 if(mResamplers
.empty())
722 mResamplers
.emplace_back();
727 ALsizei
ContextImpl::getDefaultResamplerIndex() const
730 if(!hasExtension(SOFT_source_resampler
))
732 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
736 BufferOrExceptT
ContextImpl::doCreateBuffer(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
738 BufferOrExceptT retval
;
739 ALuint srate
= decoder
->getFrequency();
740 ChannelConfig chans
= decoder
->getChannelConfig();
741 SampleType type
= decoder
->getSampleType();
742 ALuint frames
= decoder
->getLength();
744 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
745 frames
= decoder
->read(data
.data(), frames
);
746 if(!frames
) return (retval
= std::runtime_error("No samples for buffer"));
747 data
.resize(FramesToBytes(frames
, chans
, type
));
749 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
750 if(loop_pts
.first
>= loop_pts
.second
)
751 loop_pts
= std::make_pair(0, frames
);
754 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
755 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
758 // Get the format before calling the bufferLoading message handler, to
759 // ensure it's something OpenAL can handle.
760 ALenum format
= GetFormat(chans
, type
);
761 if(format
== AL_NONE
)
763 std::stringstream sstr
;
764 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
765 return (retval
= std::runtime_error(sstr
.str()));
769 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
773 alGenBuffers(1, &bid
);
774 alBufferData(bid
, format
, data
.data(), data
.size(), srate
);
775 if(hasExtension(SOFT_loop_points
))
777 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
778 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
780 if(alGetError() != AL_NO_ERROR
)
782 alDeleteBuffers(1, &bid
);
783 return (retval
= std::runtime_error("Failed to buffer data"));
786 return (retval
= mBuffers
.insert(iter
,
787 MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
)
791 BufferOrExceptT
ContextImpl::doCreateBufferAsync(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
, Promise
<Buffer
> promise
)
793 BufferOrExceptT retval
;
794 ALuint srate
= decoder
->getFrequency();
795 ChannelConfig chans
= decoder
->getChannelConfig();
796 SampleType type
= decoder
->getSampleType();
797 ALuint frames
= decoder
->getLength();
798 if(!frames
) return (retval
= std::runtime_error("No samples for buffer"));
800 ALenum format
= GetFormat(chans
, type
);
801 if(format
== AL_NONE
)
803 std::stringstream sstr
;
804 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
805 return (retval
= std::runtime_error(sstr
.str()));
810 alGenBuffers(1, &bid
);
811 if(alGetError() != AL_NO_ERROR
)
812 return (retval
= std::runtime_error("Failed to create buffer"));
814 auto buffer
= MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
);
816 if(mThread
.get_id() == std::thread::id())
817 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
819 PendingBuffer
*pb
= nullptr;
820 if(mPendingTail
== mPendingCurrent
.load(std::memory_order_acquire
))
821 pb
= new PendingBuffer
{buffer
.get(), decoder
, format
, frames
, std::move(promise
)};
825 pb
->mBuffer
= buffer
.get();
826 pb
->mDecoder
= decoder
;
827 pb
->mFormat
= format
;
828 pb
->mFrames
= frames
;
829 pb
->mPromise
= std::move(promise
);
830 mPendingTail
= pb
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
833 mPendingHead
->mNext
.store(pb
, std::memory_order_release
);
836 return (retval
= mBuffers
.insert(iter
, std::move(buffer
))->get());
839 Buffer
ContextImpl::getBuffer(StringView name
)
843 auto hasher
= std::hash
<StringView
>();
844 if(Expect
<false>(!mFutureBuffers
.empty()))
848 // If the buffer is already pending for the future, wait for it
849 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
850 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
851 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
853 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
855 buffer
= iter
->mFuture
.get();
856 mFutureBuffers
.erase(iter
);
859 // Clear out any completed futures.
860 mFutureBuffers
.erase(
861 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
862 [](const PendingFuture
&entry
) -> bool
863 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
864 ), mFutureBuffers
.end()
867 // If we got the buffer, return it. Otherwise, go load it normally.
868 if(buffer
) return buffer
;
871 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
872 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
873 { return hasher(lhs
->getName()) < rhs
; }
875 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
876 return Buffer(iter
->get());
878 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, createDecoder(name
));
879 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
880 if(Expect
<false>(!buffer
))
881 throw std::get
<std::runtime_error
>(ret
);
885 SharedFuture
<Buffer
> ContextImpl::getBufferAsync(StringView name
)
887 SharedFuture
<Buffer
> future
;
890 auto hasher
= std::hash
<StringView
>();
891 if(Expect
<false>(!mFutureBuffers
.empty()))
893 // Check if the future that's being created already exists
894 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
895 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
896 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
898 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
900 future
= iter
->mFuture
;
901 if(future
.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready
)
902 mFutureBuffers
.erase(iter
);
906 // Clear out any fulfilled futures.
907 mFutureBuffers
.erase(
908 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
909 [](const PendingFuture
&entry
) -> bool
910 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
911 ), mFutureBuffers
.end()
915 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
916 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
917 { return hasher(lhs
->getName()) < rhs
; }
919 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
921 // User asked to create a future buffer that's already loaded. Just
922 // construct a promise, fulfill the promise immediately, then return a
923 // shared future that's already set.
924 Promise
<Buffer
> promise
;
925 promise
.set_value(Buffer(iter
->get()));
926 return promise
.get_future().share();
929 Promise
<Buffer
> promise
;
930 future
= promise
.get_future().share();
932 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, createDecoder(name
), std::move(promise
));
933 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
934 if(Expect
<false>(!buffer
))
935 throw std::get
<std::runtime_error
>(ret
);
936 mWakeMutex
.lock(); mWakeMutex
.unlock();
937 mWakeThread
.notify_all();
939 mFutureBuffers
.insert(
940 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
941 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
942 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
943 ), { buffer
->getHandle(), future
}
949 void ContextImpl::precacheBuffersAsync(ArrayView
<StringView
> names
)
953 if(Expect
<false>(!mFutureBuffers
.empty()))
955 // Clear out any fulfilled futures.
956 mFutureBuffers
.erase(
957 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
958 [](const PendingFuture
&entry
) -> bool
959 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
960 ), mFutureBuffers
.end()
964 auto hasher
= std::hash
<StringView
>();
965 for(const StringView name
: names
)
967 // Check if the buffer that's being created already exists
968 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
969 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
970 { return hasher(lhs
->getName()) < rhs
; }
972 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
975 DecoderOrExceptT dec
= findDecoder(name
);
976 SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
);
977 if(!decoder
) continue;
979 Promise
<Buffer
> promise
;
980 SharedFuture
<Buffer
> future
= promise
.get_future().share();
982 BufferOrExceptT buf
= doCreateBufferAsync(name
, iter
, std::move(*decoder
),
984 Buffer
*buffer
= std::get_if
<Buffer
>(&buf
);
985 if(Expect
<false>(!buffer
)) continue;
987 mFutureBuffers
.insert(
988 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
989 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
990 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
991 ), { buffer
->getHandle(), future
}
994 mWakeMutex
.lock(); mWakeMutex
.unlock();
995 mWakeThread
.notify_all();
998 Buffer
ContextImpl::createBufferFrom(StringView name
, SharedPtr
<Decoder
> decoder
)
1002 auto hasher
= std::hash
<StringView
>();
1003 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1004 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1005 { return hasher(lhs
->getName()) < rhs
; }
1007 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1008 throw std::runtime_error("Buffer \""+name
+"\" already exists");
1010 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, std::move(decoder
));
1011 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1012 if(Expect
<false>(!buffer
))
1013 throw std::get
<std::runtime_error
>(ret
);
1017 SharedFuture
<Buffer
> ContextImpl::createBufferAsyncFrom(StringView name
, SharedPtr
<Decoder
> decoder
)
1019 SharedFuture
<Buffer
> future
;
1022 if(Expect
<false>(!mFutureBuffers
.empty()))
1024 // Clear out any fulfilled futures.
1025 mFutureBuffers
.erase(
1026 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1027 [](const PendingFuture
&entry
) -> bool
1028 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
1029 ), mFutureBuffers
.end()
1033 auto hasher
= std::hash
<StringView
>();
1034 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1035 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1036 { return hasher(lhs
->getName()) < rhs
; }
1038 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1039 throw std::runtime_error("Buffer \""+name
+"\" already exists");
1041 Promise
<Buffer
> promise
;
1042 future
= promise
.get_future().share();
1044 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, std::move(decoder
), std::move(promise
));
1045 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1046 if(Expect
<false>(!buffer
))
1047 throw std::get
<std::runtime_error
>(ret
);
1048 mWakeMutex
.lock(); mWakeMutex
.unlock();
1049 mWakeThread
.notify_all();
1051 mFutureBuffers
.insert(
1052 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1053 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
1054 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1055 ), { buffer
->getHandle(), future
}
1062 void ContextImpl::removeBuffer(StringView name
)
1066 auto hasher
= std::hash
<StringView
>();
1067 if(Expect
<false>(!mFutureBuffers
.empty()))
1069 // If the buffer is already pending for the future, wait for it to
1070 // finish before continuing.
1071 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1072 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
1073 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1075 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1077 iter
->mFuture
.wait();
1078 mFutureBuffers
.erase(iter
);
1081 // Clear out any completed futures.
1082 mFutureBuffers
.erase(
1083 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1084 [](const PendingFuture
&entry
) -> bool
1085 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
1086 ), mFutureBuffers
.end()
1090 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1091 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1092 { return hasher(lhs
->getName()) < rhs
; }
1094 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1096 // Remove pending sources whose future was waiting for this buffer.
1097 mPendingSources
.erase(
1098 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1099 [iter
](PendingSource
&entry
) -> bool
1101 return (entry
.mFuture
.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready
&&
1102 entry
.mFuture
.get().getHandle() == iter
->get());
1104 ), mPendingSources
.end()
1107 mBuffers
.erase(iter
);
1112 ALuint
ContextImpl::getSourceId(ALuint maxprio
)
1115 if(mSourceIds
.empty())
1118 alGenSources(1, &id
);
1119 if(alGetError() == AL_NO_ERROR
)
1122 SourceImpl
*lowest
= nullptr;
1123 for(SourceBufferUpdateEntry
&entry
: mPlaySources
)
1125 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1126 lowest
= entry
.mSource
;
1128 for(SourceStreamUpdateEntry
&entry
: mStreamSources
)
1130 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1131 lowest
= entry
.mSource
;
1133 if(lowest
&& lowest
->getPriority() < maxprio
)
1137 mMessage
->sourceForceStopped(lowest
);
1140 if(mSourceIds
.empty())
1141 throw std::runtime_error("No available sources");
1143 id
= mSourceIds
.top();
1149 Source
ContextImpl::createSource()
1154 if(!mFreeSources
.empty())
1156 source
= mFreeSources
.back();
1157 mFreeSources
.pop_back();
1161 mAllSources
.emplace_back(this);
1162 source
= &mAllSources
.back();
1164 return Source(source
);
1168 void ContextImpl::addPendingSource(SourceImpl
*source
, SharedFuture
<Buffer
> future
)
1170 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1171 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1172 { return lhs
.mSource
< rhs
; }
1174 if(iter
== mPendingSources
.end() || iter
->mSource
!= source
)
1175 mPendingSources
.insert(iter
, {source
, std::move(future
)});
1178 void ContextImpl::removePendingSource(SourceImpl
*source
)
1180 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1181 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1182 { return lhs
.mSource
< rhs
; }
1184 if(iter
!= mPendingSources
.end() && iter
->mSource
== source
)
1185 mPendingSources
.erase(iter
);
1188 bool ContextImpl::isPendingSource(const SourceImpl
*source
) const
1190 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1191 [](const PendingSource
&lhs
, const SourceImpl
*rhs
) -> bool
1192 { return lhs
.mSource
< rhs
; }
1194 return (iter
!= mPendingSources
.end() && iter
->mSource
== source
);
1197 void ContextImpl::addFadingSource(SourceImpl
*source
)
1199 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1200 [](SourceImpl
*lhs
, SourceImpl
*rhs
) -> bool
1201 { return lhs
< rhs
; }
1203 if(iter
== mFadingSources
.end() || *iter
!= source
)
1204 mFadingSources
.insert(iter
, source
);
1207 void ContextImpl::removeFadingSource(SourceImpl
*source
)
1209 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1210 [](SourceImpl
*lhs
, SourceImpl
*rhs
) -> bool
1211 { return lhs
< rhs
; }
1213 if(iter
!= mFadingSources
.end() && *iter
== source
)
1214 mFadingSources
.erase(iter
);
1217 void ContextImpl::addPlayingSource(SourceImpl
*source
, ALuint id
)
1219 auto iter
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1220 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1221 { return lhs
.mSource
< rhs
; }
1223 if(iter
== mPlaySources
.end() || iter
->mSource
!= source
)
1224 mPlaySources
.insert(iter
, {source
,id
});
1227 void ContextImpl::addPlayingSource(SourceImpl
*source
)
1229 auto iter
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1230 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1231 { return lhs
.mSource
< rhs
; }
1233 if(iter
== mStreamSources
.end() || iter
->mSource
!= source
)
1234 mStreamSources
.insert(iter
, {source
});
1237 void ContextImpl::removePlayingSource(SourceImpl
*source
)
1239 auto iter0
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1240 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1241 { return lhs
.mSource
< rhs
; }
1243 if(iter0
!= mPlaySources
.end() && iter0
->mSource
== source
)
1244 mPlaySources
.erase(iter0
);
1247 auto iter1
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1248 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1249 { return lhs
.mSource
< rhs
; }
1251 if(iter1
!= mStreamSources
.end() && iter1
->mSource
== source
)
1252 mStreamSources
.erase(iter1
);
1257 void ContextImpl::addStream(SourceImpl
*source
)
1259 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1260 if(mThread
.get_id() == std::thread::id())
1261 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
1262 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1263 if(iter
== mStreamingSources
.end() || *iter
!= source
)
1264 mStreamingSources
.insert(iter
, source
);
1267 void ContextImpl::removeStream(SourceImpl
*source
)
1269 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1270 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1271 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1272 mStreamingSources
.erase(iter
);
1275 void ContextImpl::removeStreamNoLock(SourceImpl
*source
)
1277 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1278 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1279 mStreamingSources
.erase(iter
);
1283 AuxiliaryEffectSlot
ContextImpl::createAuxiliaryEffectSlot()
1285 if(!hasExtension(EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
1286 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1291 alGenAuxiliaryEffectSlots(1, &id
);
1292 if(alGetError() != AL_NO_ERROR
)
1293 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1295 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id
));
1298 alDeleteAuxiliaryEffectSlots(1, &id
);
1304 Effect
ContextImpl::createEffect()
1306 if(!hasExtension(EXT_EFX
))
1307 throw std::runtime_error("Effects not supported");
1312 alGenEffects(1, &id
);
1313 if(alGetError() != AL_NO_ERROR
)
1314 throw std::runtime_error("Failed to create Effect");
1316 return Effect(new EffectImpl(this, id
));
1319 alDeleteEffects(1, &id
);
1325 SourceGroup
ContextImpl::createSourceGroup(StringView name
)
1327 auto hasher
= std::hash
<StringView
>();
1328 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), hasher(name
),
1329 [hasher
](const UniquePtr
<SourceGroupImpl
> &lhs
, size_t rhs
) -> bool
1330 { return hasher(lhs
->getName()) < rhs
; }
1332 if(iter
!= mSourceGroups
.end() && (*iter
)->getName() == name
)
1333 throw std::runtime_error("Duplicate source group name");
1334 iter
= mSourceGroups
.insert(iter
, MakeUnique
<SourceGroupImpl
>(this, String(name
)));
1335 return SourceGroup(iter
->get());
1338 SourceGroup
ContextImpl::getSourceGroup(StringView name
)
1340 auto hasher
= std::hash
<StringView
>();
1341 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), hasher(name
),
1342 [hasher
](const UniquePtr
<SourceGroupImpl
> &lhs
, size_t rhs
) -> bool
1343 { return hasher(lhs
->getName()) < rhs
; }
1345 if(iter
== mSourceGroups
.end() || (*iter
)->getName() != name
)
1346 throw std::runtime_error("Source group not found");
1347 return SourceGroup(iter
->get());
1350 void ContextImpl::freeSourceGroup(SourceGroupImpl
*group
)
1352 auto hasher
= std::hash
<StringView
>();
1353 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), hasher(group
->getName()),
1354 [hasher
](const UniquePtr
<SourceGroupImpl
> &lhs
, size_t rhs
) -> bool
1355 { return hasher(lhs
->getName()) < rhs
; }
1357 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1358 mSourceGroups
.erase(iter
);
1362 void ContextImpl::setDopplerFactor(ALfloat factor
)
1364 if(!(factor
>= 0.0f
))
1365 throw std::runtime_error("Doppler factor out of range");
1367 alDopplerFactor(factor
);
1371 void ContextImpl::setSpeedOfSound(ALfloat speed
)
1374 throw std::runtime_error("Speed of sound out of range");
1376 alSpeedOfSound(speed
);
1380 void ContextImpl::setDistanceModel(DistanceModel model
)
1383 alDistanceModel((ALenum
)model
);
1387 void ContextImpl::update()
1390 mPendingSources
.erase(
1391 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1392 [](PendingSource
&entry
) -> bool
1393 { return !entry
.mSource
->checkPending(entry
.mFuture
); }
1394 ), mPendingSources
.end()
1396 if(!mFadingSources
.empty())
1398 auto cur_time
= std::chrono::steady_clock::now();
1399 mFadingSources
.erase(
1400 std::remove_if(mFadingSources
.begin(), mFadingSources
.end(),
1401 [cur_time
](SourceImpl
*source
) -> bool
1402 { return !source
->fadeUpdate(cur_time
); }
1403 ), mFadingSources
.end()
1407 std::remove_if(mPlaySources
.begin(), mPlaySources
.end(),
1408 [](const SourceBufferUpdateEntry
&entry
) -> bool
1409 { return !entry
.mSource
->playUpdate(entry
.mId
); }
1410 ), mPlaySources
.end()
1412 mStreamSources
.erase(
1413 std::remove_if(mStreamSources
.begin(), mStreamSources
.end(),
1414 [](const SourceStreamUpdateEntry
&entry
) -> bool
1415 { return !entry
.mSource
->playUpdate(); }
1416 ), mStreamSources
.end()
1419 if(!mWakeInterval
.load(std::memory_order_relaxed
).count())
1421 // For performance reasons, don't wait for the thread's mutex. This
1422 // should be called often enough to keep up with any and all streams
1424 mWakeThread
.notify_all();
1427 if(hasExtension(EXT_disconnect
) && mIsConnected
)
1430 alcGetIntegerv(alcGetContextsDevice(mContext
), ALC_CONNECTED
, 1, &connected
);
1431 mIsConnected
= connected
;
1432 if(!connected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(mDevice
));
1436 void Context::destroy()
1441 DECL_THUNK0(Device
, Context
, getDevice
,)
1442 DECL_THUNK0(void, Context
, startBatch
,)
1443 DECL_THUNK0(void, Context
, endBatch
,)
1444 DECL_THUNK0(Listener
, Context
, getListener
,)
1445 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
1446 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1447 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
1448 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1449 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, StringView
)
1450 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
1451 DECL_THUNK0(ArrayView
<String
>, Context
, getAvailableResamplers
,)
1452 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
1453 DECL_THUNK1(Buffer
, Context
, getBuffer
,, StringView
)
1454 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, getBufferAsync
,, StringView
)
1455 DECL_THUNK1(void, Context
, precacheBuffersAsync
,, ArrayView
<StringView
>)
1456 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, StringView
, SharedPtr
<Decoder
>)
1457 DECL_THUNK2(SharedFuture
<Buffer
>, Context
, createBufferAsyncFrom
,, StringView
, SharedPtr
<Decoder
>)
1458 DECL_THUNK1(void, Context
, removeBuffer
,, StringView
)
1459 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1460 DECL_THUNK0(Source
, Context
, createSource
,)
1461 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1462 DECL_THUNK0(Effect
, Context
, createEffect
,)
1463 DECL_THUNK1(SourceGroup
, Context
, createSourceGroup
,, StringView
)
1464 DECL_THUNK1(SourceGroup
, Context
, getSourceGroup
,, StringView
)
1465 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1466 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1467 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1468 DECL_THUNK0(void, Context
, update
,)
1471 void Context::MakeCurrent(Context context
)
1472 { ContextImpl::MakeCurrent(context
.pImpl
); }
1474 Context
Context::GetCurrent()
1475 { return Context(ContextImpl::GetCurrent()); }
1477 void Context::MakeThreadCurrent(Context context
)
1478 { ContextImpl::MakeThreadCurrent(context
.pImpl
); }
1480 Context
Context::GetThreadCurrent()
1481 { return Context(ContextImpl::GetThreadCurrent()); }
1484 void ListenerImpl::setGain(ALfloat gain
)
1487 throw std::runtime_error("Gain out of range");
1488 CheckContext(mContext
);
1489 alListenerf(AL_GAIN
, gain
);
1493 void ListenerImpl::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, std::pair
<Vector3
,Vector3
> orientation
)
1495 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1496 CheckContext(mContext
);
1497 Batcher batcher
= mContext
->getBatcher();
1498 alListenerfv(AL_POSITION
, position
.getPtr());
1499 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1500 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1503 void ListenerImpl::setPosition(ALfloat x
, ALfloat y
, ALfloat z
)
1505 CheckContext(mContext
);
1506 alListener3f(AL_POSITION
, x
, y
, z
);
1509 void ListenerImpl::setPosition(const ALfloat
*pos
)
1511 CheckContext(mContext
);
1512 alListenerfv(AL_POSITION
, pos
);
1515 void ListenerImpl::setVelocity(ALfloat x
, ALfloat y
, ALfloat z
)
1517 CheckContext(mContext
);
1518 alListener3f(AL_VELOCITY
, x
, y
, z
);
1521 void ListenerImpl::setVelocity(const ALfloat
*vel
)
1523 CheckContext(mContext
);
1524 alListenerfv(AL_VELOCITY
, vel
);
1527 void ListenerImpl::setOrientation(ALfloat x1
, ALfloat y1
, ALfloat z1
, ALfloat x2
, ALfloat y2
, ALfloat z2
)
1529 CheckContext(mContext
);
1530 ALfloat ori
[6] = { x1
, y1
, z1
, x2
, y2
, z2
};
1531 alListenerfv(AL_ORIENTATION
, ori
);
1534 void ListenerImpl::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1536 CheckContext(mContext
);
1537 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1538 alListenerfv(AL_ORIENTATION
, ori
);
1541 void ListenerImpl::setOrientation(const ALfloat
*ori
)
1543 CheckContext(mContext
);
1544 alListenerfv(AL_ORIENTATION
, ori
);
1547 void ListenerImpl::setMetersPerUnit(ALfloat m_u
)
1550 throw std::runtime_error("Invalid meters per unit");
1551 CheckContext(mContext
);
1552 if(mContext
->hasExtension(EXT_EFX
))
1553 alListenerf(AL_METERS_PER_UNIT
, m_u
);
1557 using Vector3Pair
= std::pair
<Vector3
,Vector3
>;
1559 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1560 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, Vector3Pair
)
1561 DECL_THUNK3(void, Listener
, setPosition
,, ALfloat
, ALfloat
, ALfloat
)
1562 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1563 DECL_THUNK3(void, Listener
, setVelocity
,, ALfloat
, ALfloat
, ALfloat
)
1564 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1565 DECL_THUNK6(void, Listener
, setOrientation
,, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
)
1566 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1567 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1568 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)