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 if /*constexpr*/ (sizeof(size_t) == 8)
64 static constexpr size_t hash_offset
= 0xcbf29ce484222325;
65 static constexpr size_t hash_prime
= 0x100000001b3;
67 size_t val
= hash_offset
;
69 val
= (val
^static_cast<unsigned char>(ch
)) * hash_prime
;
74 static constexpr size_t hash_offset
= 0x811c9dc5;
75 static constexpr size_t hash_prime
= 0x1000193;
77 size_t val
= hash_offset
;
79 val
= (val
^static_cast<unsigned char>(ch
)) * hash_prime
;
90 // Global mutex to protect global context changes
91 std::mutex mGlobalCtxMutex
;
94 // Windows' std::ifstream fails with non-ANSI paths since the standard only
95 // specifies names using const char* (or std::string). MSVC has a non-standard
96 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
97 // but not all Windows compilers support it. So we have to make our own istream
98 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
99 class StreamBuf final
: public std::streambuf
{
100 alure::Array
<char_type
,4096> mBuffer
;
103 int_type
underflow() override
105 if(mFile
!= INVALID_HANDLE_VALUE
&& gptr() == egptr())
107 // Read in the next chunk of data, and set the pointers on success
109 if(!ReadFile(mFile
, mBuffer
.data(), mBuffer
.size(), &got
, NULL
))
111 setg(mBuffer
.data(), mBuffer
.data(), mBuffer
.data()+got
);
113 if(gptr() == egptr())
114 return traits_type::eof();
115 return traits_type::to_int_type(*gptr());
118 pos_type
seekoff(off_type offset
, std::ios_base::seekdir whence
, std::ios_base::openmode mode
) override
120 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
121 return traits_type::eof();
126 case std::ios_base::beg
:
127 fpos
.QuadPart
= offset
;
128 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
129 return traits_type::eof();
132 case std::ios_base::cur
:
133 // If the offset remains in the current buffer range, just
134 // update the pointer.
135 if((offset
>= 0 && offset
< off_type(egptr()-gptr())) ||
136 (offset
< 0 && -offset
<= off_type(gptr()-eback())))
138 // Get the current file offset to report the correct read
141 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
142 return traits_type::eof();
143 setg(eback(), gptr()+offset
, egptr());
144 return fpos
.QuadPart
- off_type(egptr()-gptr());
146 // Need to offset for the file offset being at egptr() while
147 // the requested offset is relative to gptr().
148 offset
-= off_type(egptr()-gptr());
149 fpos
.QuadPart
= offset
;
150 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
151 return traits_type::eof();
154 case std::ios_base::end
:
155 fpos
.QuadPart
= offset
;
156 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_END
))
157 return traits_type::eof();
161 return traits_type::eof();
164 return fpos
.QuadPart
;
167 pos_type
seekpos(pos_type pos
, std::ios_base::openmode mode
) override
169 // Simplified version of seekoff
170 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
171 return traits_type::eof();
175 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
176 return traits_type::eof();
179 return fpos
.QuadPart
;
183 bool open(const char *filename
)
185 alure::Vector
<wchar_t> wname
;
188 wnamelen
= MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, NULL
, 0);
189 if(wnamelen
<= 0) return false;
191 wname
.resize(wnamelen
);
192 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, wname
.data(), wnamelen
);
194 mFile
= CreateFileW(wname
.data(), GENERIC_READ
, FILE_SHARE_READ
, NULL
,
195 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, nullptr);
196 if(mFile
== INVALID_HANDLE_VALUE
) return false;
200 bool is_open() const { return mFile
!= INVALID_HANDLE_VALUE
; }
202 StreamBuf() : mFile(INVALID_HANDLE_VALUE
)
204 ~StreamBuf() override
206 if(mFile
!= INVALID_HANDLE_VALUE
)
208 mFile
= INVALID_HANDLE_VALUE
;
212 // Inherit from std::istream to use our custom streambuf
213 class Stream final
: public std::istream
{
215 Stream(const char *filename
) : std::istream(new StreamBuf())
217 // Set the failbit if the file failed to open.
218 if(!(static_cast<StreamBuf
*>(rdbuf())->open(filename
)))
224 bool is_open() const { return static_cast<StreamBuf
*>(rdbuf())->is_open(); }
228 using DecoderEntryPair
= std::pair
<alure::String
,alure::UniquePtr
<alure::DecoderFactory
>>;
229 const DecoderEntryPair sDefaultDecoders
[] = {
230 { "_alure_int_wave", alure::MakeUnique
<alure::WaveDecoderFactory
>() },
232 #ifdef HAVE_VORBISFILE
233 { "_alure_int_vorbis", alure::MakeUnique
<alure::VorbisFileDecoderFactory
>() },
236 { "_alure_int_flac", alure::MakeUnique
<alure::FlacDecoderFactory
>() },
239 { "_alure_int_opus", alure::MakeUnique
<alure::OpusFileDecoderFactory
>() },
241 #ifdef HAVE_LIBSNDFILE
242 { "_alure_int_sndfile", alure::MakeUnique
<alure::SndFileDecoderFactory
>() },
245 { "_alure_int_mpg123", alure::MakeUnique
<alure::Mpg123DecoderFactory
>() },
248 alure::Vector
<DecoderEntryPair
> sDecoders
;
252 alure::DecoderOrExceptT
GetDecoder(alure::StringView name
, alure::UniquePtr
<std::istream
> &file
,
255 alure::DecoderOrExceptT ret
;
258 alure::DecoderFactory
*factory
= start
->second
.get();
259 auto decoder
= factory
->createDecoder(file
);
260 if(decoder
) return (ret
= std::move(decoder
));
262 if(!file
|| !(file
->clear(),file
->seekg(0)))
263 return (ret
= std::runtime_error("Failed to rewind "+name
+" for the next decoder factory"));
268 return (ret
= nullptr);
271 static alure::DecoderOrExceptT
GetDecoder(alure::StringView name
, alure::UniquePtr
<std::istream
> file
)
273 auto decoder
= GetDecoder(name
, file
, sDecoders
.begin(), sDecoders
.end());
274 if(std::holds_alternative
<std::runtime_error
>(decoder
)) return decoder
;
275 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
276 decoder
= GetDecoder(name
, file
, std::begin(sDefaultDecoders
), std::end(sDefaultDecoders
));
277 if(std::holds_alternative
<std::runtime_error
>(decoder
)) return decoder
;
278 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
279 return (decoder
= std::runtime_error("No decoder for "+name
));
282 class DefaultFileIOFactory final
: public alure::FileIOFactory
{
283 alure::UniquePtr
<std::istream
> openFile(const alure::String
&name
) override
286 auto file
= alure::MakeUnique
<Stream
>(name
.c_str());
288 auto file
= alure::MakeUnique
<std::ifstream
>(name
.c_str(), std::ios::binary
);
290 if(!file
->is_open()) file
= nullptr;
291 return std::move(file
);
294 DefaultFileIOFactory sDefaultFileFactory
;
296 alure::UniquePtr
<alure::FileIOFactory
> sFileFactory
;
303 Decoder::~Decoder() { }
304 DecoderFactory::~DecoderFactory() { }
306 void RegisterDecoder(const String
&name
, UniquePtr
<DecoderFactory
> factory
)
308 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
309 [](const DecoderEntryPair
&entry
, const String
&rhs
) -> bool
310 { return entry
.first
< rhs
; }
312 if(iter
!= sDecoders
.end())
313 throw std::runtime_error("Decoder factory \""+name
+"\" already registered");
314 sDecoders
.insert(iter
, std::make_pair(name
, std::move(factory
)));
317 UniquePtr
<DecoderFactory
> UnregisterDecoder(const String
&name
)
319 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
320 [](const DecoderEntryPair
&entry
, const String
&rhs
) -> bool
321 { return entry
.first
< rhs
; }
323 if(iter
!= sDecoders
.end())
325 UniquePtr
<DecoderFactory
> factory
= std::move(iter
->second
);
326 sDecoders
.erase(iter
);
333 FileIOFactory::~FileIOFactory() { }
335 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
)
337 std::swap(sFileFactory
, factory
);
341 FileIOFactory
&FileIOFactory::get()
343 FileIOFactory
*factory
= sFileFactory
.get();
344 if(factory
) return *factory
;
345 return sDefaultFileFactory
;
349 // Default message handler methods are no-ops.
350 MessageHandler::~MessageHandler()
354 void MessageHandler::deviceDisconnected(Device
)
358 void MessageHandler::sourceStopped(Source
)
362 void MessageHandler::sourceForceStopped(Source
)
366 void MessageHandler::bufferLoading(StringView
, ChannelConfig
, SampleType
, ALuint
, ArrayView
<ALbyte
>)
370 String
MessageHandler::resourceNotFound(StringView
)
377 static inline void LoadALFunc(T
**func
, const char *name
)
378 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
380 static void LoadNothing(ContextImpl
*) { }
382 static void LoadEFX(ContextImpl
*ctx
)
384 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
385 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
386 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
387 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
388 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
389 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
390 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
391 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
392 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
393 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
394 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
396 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
397 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
398 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
399 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
400 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
401 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
402 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
403 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
404 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
405 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
406 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
408 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
409 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
410 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
411 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
412 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
413 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
414 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
415 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
416 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
417 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
418 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
421 static void LoadSourceResampler(ContextImpl
*ctx
)
423 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
426 static void LoadSourceLatency(ContextImpl
*ctx
)
428 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
429 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
432 static const struct {
433 enum ALExtension extension
;
435 void (&loader
)(ContextImpl
*);
436 } ALExtensionList
[] = {
437 { EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
439 { EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
440 { EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
441 { EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
443 { EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
444 { EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
445 { EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
447 { SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
448 { SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
449 { SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
450 { SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
452 { EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
454 { EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
455 { EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
459 ContextImpl
*ContextImpl::sCurrentCtx
= nullptr;
460 thread_local ContextImpl
*ContextImpl::sThreadCurrentCtx
= nullptr;
462 void ContextImpl::MakeCurrent(ContextImpl
*context
)
464 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
466 if(alcMakeContextCurrent(context
? context
->getContext() : nullptr) == ALC_FALSE
)
467 throw std::runtime_error("Call to alcMakeContextCurrent failed");
471 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
473 std::swap(sCurrentCtx
, context
);
474 if(context
) context
->decRef();
476 if(sThreadCurrentCtx
)
477 sThreadCurrentCtx
->decRef();
478 sThreadCurrentCtx
= 0;
480 if((context
= sCurrentCtx
) != nullptr)
483 context
->mWakeThread
.notify_all();
487 void ContextImpl::MakeThreadCurrent(ContextImpl
*context
)
489 if(!DeviceManagerImpl::SetThreadContext
)
490 throw std::runtime_error("Thread-local contexts unsupported");
491 if(DeviceManagerImpl::SetThreadContext(context
? context
->getContext() : nullptr) == ALC_FALSE
)
492 throw std::runtime_error("Call to alcSetThreadContext failed");
496 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
498 if(sThreadCurrentCtx
)
499 sThreadCurrentCtx
->decRef();
500 sThreadCurrentCtx
= context
;
503 void ContextImpl::setupExts()
505 ALCdevice
*device
= mDevice
->getDevice();
506 std::fill(std::begin(mHasExt
), std::end(mHasExt
), false);
507 for(const auto &entry
: ALExtensionList
)
509 mHasExt
[entry
.extension
] = (strncmp(entry
.name
, "ALC", 3) == 0) ?
510 alcIsExtensionPresent(device
, entry
.name
) :
511 alIsExtensionPresent(entry
.name
);
512 if(mHasExt
[entry
.extension
]) entry
.loader(this);
517 void ContextImpl::backgroundProc()
519 if(DeviceManagerImpl::SetThreadContext
&& mDevice
->hasExtension(EXT_thread_local_context
))
520 DeviceManagerImpl::SetThreadContext(getContext());
522 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
523 std::chrono::milliseconds
waketime(0);
524 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
525 while(!mQuitThread
.load(std::memory_order_acquire
))
528 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
529 mStreamingSources
.erase(
530 std::remove_if(mStreamingSources
.begin(), mStreamingSources
.end(),
531 [](SourceImpl
*source
) -> bool
532 { return !source
->updateAsync(); }
533 ), mStreamingSources
.end()
537 // Only do one pending buffer at a time. In case there's several large
538 // buffers to load, we still need to process streaming sources so they
540 RingBuffer::Data ringdata
= mPendingBuffers
.get_read_vector()[0];
543 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
.buf
);
544 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, pb
->mDecoder
, this);
545 pb
->mPromise
.set_value(Buffer(pb
->mBuffer
));
546 pb
->~PendingBuffer();
547 mPendingBuffers
.read_advance(1);
551 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
552 if(!mQuitThread
.load(std::memory_order_acquire
) && mPendingBuffers
.read_space() == 0)
556 std::chrono::milliseconds interval
= mWakeInterval
.load(std::memory_order_relaxed
);
557 if(interval
.count() == 0)
558 mWakeThread
.wait(wakelock
);
561 auto now
= std::chrono::steady_clock::now() - basetime
;
564 auto mult
= (now
-waketime
+ interval
-std::chrono::milliseconds(1)) / interval
;
565 waketime
+= interval
* mult
;
566 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
572 while(!mQuitThread
.load(std::memory_order_acquire
) &&
573 alcGetCurrentContext() != getContext())
574 mWakeThread
.wait(ctxlock
);
579 if(DeviceManagerImpl::SetThreadContext
)
580 DeviceManagerImpl::SetThreadContext(nullptr);
584 ContextImpl::ContextImpl(ALCcontext
*context
, DeviceImpl
*device
)
585 : mListener(this), mContext(context
), mDevice(device
),
586 mWakeInterval(std::chrono::milliseconds::zero()),
587 mPendingBuffers(16, sizeof(PendingBuffer
)), 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)
602 ContextImpl::~ContextImpl()
604 auto ringdata
= mPendingBuffers
.get_read_vector();
605 if(ringdata
[0].len
> 0)
607 PendingBuffer
*pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[0].buf
);
608 for(size_t i
= 0;i
< ringdata
[0].len
;i
++)
609 pb
[i
].~PendingBuffer();
610 pb
= reinterpret_cast<PendingBuffer
*>(ringdata
[1].buf
);
611 for(size_t i
= 0;i
< ringdata
[1].len
;i
++)
612 pb
[i
].~PendingBuffer();
614 mPendingBuffers
.read_advance(ringdata
[0].len
+ ringdata
[1].len
);
619 void ContextImpl::destroy()
621 if(mRefs
.load() != 0)
622 throw std::runtime_error("Context is in use");
623 if(!mBuffers
.empty())
624 throw std::runtime_error("Trying to destroy a context with buffers");
626 if(mThread
.joinable())
628 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
629 mQuitThread
.store(true, std::memory_order_release
);
631 mWakeThread
.notify_all();
635 alcDestroyContext(mContext
);
638 mDevice
->removeContext(this);
642 void ContextImpl::startBatch()
644 alcSuspendContext(mContext
);
648 void ContextImpl::endBatch()
650 alcProcessContext(mContext
);
655 SharedPtr
<MessageHandler
> ContextImpl::setMessageHandler(SharedPtr
<MessageHandler
> handler
)
657 std::lock_guard
<std::mutex
> lock(mGlobalCtxMutex
);
658 mMessage
.swap(handler
);
663 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval
)
665 if(interval
.count() < 0 || interval
> std::chrono::seconds(1))
666 throw std::runtime_error("Async wake interval out of range");
667 mWakeInterval
.store(interval
);
668 mWakeMutex
.lock(); mWakeMutex
.unlock();
669 mWakeThread
.notify_all();
673 DecoderOrExceptT
ContextImpl::findDecoder(StringView name
)
675 DecoderOrExceptT ret
;
677 String oldname
= String(name
);
678 auto file
= FileIOFactory::get().openFile(oldname
);
679 if(file
) return (ret
= GetDecoder(name
, std::move(file
)));
681 // Resource not found. Try to find a substitute.
682 if(!mMessage
.get()) return (ret
= std::runtime_error("Failed to open "+oldname
));
684 String
newname(mMessage
->resourceNotFound(oldname
));
686 return (ret
= std::runtime_error("Failed to open "+oldname
));
687 file
= FileIOFactory::get().openFile(newname
);
688 oldname
= std::move(newname
);
691 return (ret
= GetDecoder(oldname
, std::move(file
)));
694 SharedPtr
<Decoder
> ContextImpl::createDecoder(StringView name
)
697 DecoderOrExceptT dec
= findDecoder(name
);
698 if(SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
))
700 throw std::get
<std::runtime_error
>(dec
);
704 bool ContextImpl::isSupported(ChannelConfig channels
, SampleType type
) const
707 return GetFormat(channels
, type
) != AL_NONE
;
711 ArrayView
<String
> ContextImpl::getAvailableResamplers()
714 if(mResamplers
.empty() && hasExtension(SOFT_source_resampler
))
716 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
717 mResamplers
.reserve(num_resamplers
);
718 for(int i
= 0;i
< num_resamplers
;i
++)
719 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
720 if(mResamplers
.empty())
721 mResamplers
.emplace_back();
726 ALsizei
ContextImpl::getDefaultResamplerIndex() const
729 if(!hasExtension(SOFT_source_resampler
))
731 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
735 BufferOrExceptT
ContextImpl::doCreateBuffer(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
737 BufferOrExceptT retval
;
738 ALuint srate
= decoder
->getFrequency();
739 ChannelConfig chans
= decoder
->getChannelConfig();
740 SampleType type
= decoder
->getSampleType();
741 ALuint frames
= decoder
->getLength();
743 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
744 frames
= decoder
->read(data
.data(), frames
);
745 if(!frames
) return (retval
= std::runtime_error("No samples for buffer"));
746 data
.resize(FramesToBytes(frames
, chans
, type
));
748 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
749 if(loop_pts
.first
>= loop_pts
.second
)
750 loop_pts
= std::make_pair(0, frames
);
753 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
754 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
757 // Get the format before calling the bufferLoading message handler, to
758 // ensure it's something OpenAL can handle.
759 ALenum format
= GetFormat(chans
, type
);
760 if(format
== AL_NONE
)
762 std::stringstream sstr
;
763 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
764 return (retval
= std::runtime_error(sstr
.str()));
768 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
772 alGenBuffers(1, &bid
);
773 alBufferData(bid
, format
, data
.data(), data
.size(), srate
);
774 if(hasExtension(SOFT_loop_points
))
776 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
777 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
779 if(alGetError() != AL_NO_ERROR
)
781 alDeleteBuffers(1, &bid
);
782 return (retval
= std::runtime_error("Failed to buffer data"));
785 return (retval
= mBuffers
.insert(iter
,
786 MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
)
790 BufferOrExceptT
ContextImpl::doCreateBufferAsync(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
, Promise
<Buffer
> promise
)
792 BufferOrExceptT retval
;
793 ALuint srate
= decoder
->getFrequency();
794 ChannelConfig chans
= decoder
->getChannelConfig();
795 SampleType type
= decoder
->getSampleType();
796 ALuint frames
= decoder
->getLength();
797 if(!frames
) return (retval
= std::runtime_error("No samples for buffer"));
799 ALenum format
= GetFormat(chans
, type
);
800 if(format
== AL_NONE
)
802 std::stringstream sstr
;
803 sstr
<< "Format not supported ("<<GetSampleTypeName(type
)<<", "<<GetChannelConfigName(chans
)<<")";
804 return (retval
= std::runtime_error(sstr
.str()));
809 alGenBuffers(1, &bid
);
810 if(alGetError() != AL_NO_ERROR
)
811 return (retval
= std::runtime_error("Failed to create buffer"));
813 auto buffer
= MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
);
815 if(mThread
.get_id() == std::thread::id())
816 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
818 while(mPendingBuffers
.write_space() == 0)
820 mWakeThread
.notify_all();
821 std::this_thread::yield();
824 RingBuffer::Data ringdata
= mPendingBuffers
.get_write_vector()[0];
825 new(ringdata
.buf
) PendingBuffer
{buffer
.get(), decoder
, format
, frames
, std::move(promise
)};
826 mPendingBuffers
.write_advance(1);
828 return (retval
= mBuffers
.insert(iter
, std::move(buffer
))->get());
831 Buffer
ContextImpl::getBuffer(StringView name
)
835 auto hasher
= std::hash
<StringView
>();
836 if(EXPECT(!mFutureBuffers
.empty(), false))
840 // If the buffer is already pending for the future, wait for it
841 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
842 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
843 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
845 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
847 buffer
= iter
->mFuture
.get();
848 mFutureBuffers
.erase(iter
);
851 // Clear out any completed futures.
852 mFutureBuffers
.erase(
853 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
854 [](const PendingFuture
&entry
) -> bool
855 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
856 ), mFutureBuffers
.end()
859 // If we got the buffer, return it. Otherwise, go load it normally.
860 if(buffer
) return buffer
;
863 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
864 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
865 { return hasher(lhs
->getName()) < rhs
; }
867 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
868 return Buffer(iter
->get());
870 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, createDecoder(name
));
871 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
872 if(EXPECT(!buffer
, false))
873 throw std::get
<std::runtime_error
>(ret
);
877 SharedFuture
<Buffer
> ContextImpl::getBufferAsync(StringView name
)
879 SharedFuture
<Buffer
> future
;
882 auto hasher
= std::hash
<StringView
>();
883 if(EXPECT(!mFutureBuffers
.empty(), false))
885 // Check if the future that's being created already exists
886 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
887 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
888 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
890 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
892 future
= iter
->mFuture
;
893 if(future
.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready
)
894 mFutureBuffers
.erase(iter
);
898 // Clear out any fulfilled futures.
899 mFutureBuffers
.erase(
900 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
901 [](const PendingFuture
&entry
) -> bool
902 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
903 ), mFutureBuffers
.end()
907 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
908 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
909 { return hasher(lhs
->getName()) < rhs
; }
911 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
913 // User asked to create a future buffer that's already loaded. Just
914 // construct a promise, fulfill the promise immediately, then return a
915 // shared future that's already set.
916 Promise
<Buffer
> promise
;
917 promise
.set_value(Buffer(iter
->get()));
918 return promise
.get_future().share();
921 Promise
<Buffer
> promise
;
922 future
= promise
.get_future().share();
924 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, createDecoder(name
), std::move(promise
));
925 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
926 if(EXPECT(!buffer
, false))
927 throw std::get
<std::runtime_error
>(ret
);
928 mWakeMutex
.lock(); mWakeMutex
.unlock();
929 mWakeThread
.notify_all();
931 mFutureBuffers
.insert(
932 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
933 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
934 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
935 ), { buffer
->getHandle(), future
}
941 void ContextImpl::precacheBuffersAsync(ArrayView
<StringView
> names
)
945 if(EXPECT(!mFutureBuffers
.empty(), false))
947 // Clear out any fulfilled futures.
948 mFutureBuffers
.erase(
949 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
950 [](const PendingFuture
&entry
) -> bool
951 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
952 ), mFutureBuffers
.end()
956 auto hasher
= std::hash
<StringView
>();
957 for(const StringView name
: names
)
959 // Check if the buffer that's being created already exists
960 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
961 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
962 { return hasher(lhs
->getName()) < rhs
; }
964 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
967 DecoderOrExceptT dec
= findDecoder(name
);
968 SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
);
969 if(!decoder
) continue;
971 Promise
<Buffer
> promise
;
972 SharedFuture
<Buffer
> future
= promise
.get_future().share();
974 BufferOrExceptT buf
= doCreateBufferAsync(name
, iter
, std::move(*decoder
),
976 Buffer
*buffer
= std::get_if
<Buffer
>(&buf
);
977 if(EXPECT(!buffer
, false)) continue;
979 mFutureBuffers
.insert(
980 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
981 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
982 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
983 ), { buffer
->getHandle(), future
}
986 mWakeMutex
.lock(); mWakeMutex
.unlock();
987 mWakeThread
.notify_all();
990 Buffer
ContextImpl::createBufferFrom(StringView name
, SharedPtr
<Decoder
> decoder
)
994 auto hasher
= std::hash
<StringView
>();
995 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
996 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
997 { return hasher(lhs
->getName()) < rhs
; }
999 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1000 throw std::runtime_error("Buffer \""+name
+"\" already exists");
1002 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, std::move(decoder
));
1003 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1004 if(EXPECT(!buffer
, false))
1005 throw std::get
<std::runtime_error
>(ret
);
1009 SharedFuture
<Buffer
> ContextImpl::createBufferAsyncFrom(StringView name
, SharedPtr
<Decoder
> decoder
)
1011 SharedFuture
<Buffer
> future
;
1014 if(EXPECT(!mFutureBuffers
.empty(), false))
1016 // Clear out any fulfilled futures.
1017 mFutureBuffers
.erase(
1018 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1019 [](const PendingFuture
&entry
) -> bool
1020 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
1021 ), mFutureBuffers
.end()
1025 auto hasher
= std::hash
<StringView
>();
1026 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1027 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1028 { return hasher(lhs
->getName()) < rhs
; }
1030 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1031 throw std::runtime_error("Buffer \""+name
+"\" already exists");
1033 Promise
<Buffer
> promise
;
1034 future
= promise
.get_future().share();
1036 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, std::move(decoder
), std::move(promise
));
1037 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1038 if(EXPECT(!buffer
, false))
1039 throw std::get
<std::runtime_error
>(ret
);
1040 mWakeMutex
.lock(); mWakeMutex
.unlock();
1041 mWakeThread
.notify_all();
1043 mFutureBuffers
.insert(
1044 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1045 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
1046 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1047 ), { buffer
->getHandle(), future
}
1054 void ContextImpl::removeBuffer(StringView name
)
1058 auto hasher
= std::hash
<StringView
>();
1059 if(EXPECT(!mFutureBuffers
.empty(), false))
1061 // If the buffer is already pending for the future, wait for it to
1062 // finish before continuing.
1063 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1064 [hasher
](const PendingFuture
&lhs
, size_t rhs
) -> bool
1065 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1067 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1069 iter
->mFuture
.wait();
1070 mFutureBuffers
.erase(iter
);
1073 // Clear out any completed futures.
1074 mFutureBuffers
.erase(
1075 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1076 [](const PendingFuture
&entry
) -> bool
1077 { return entry
.mFuture
.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready
; }
1078 ), mFutureBuffers
.end()
1082 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1083 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1084 { return hasher(lhs
->getName()) < rhs
; }
1086 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1089 mBuffers
.erase(iter
);
1094 ALuint
ContextImpl::getSourceId(ALuint maxprio
)
1097 if(mSourceIds
.empty())
1100 alGenSources(1, &id
);
1101 if(alGetError() == AL_NO_ERROR
)
1104 SourceImpl
*lowest
= nullptr;
1105 for(SourceBufferUpdateEntry
&entry
: mPlaySources
)
1107 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1108 lowest
= entry
.mSource
;
1110 for(SourceStreamUpdateEntry
&entry
: mStreamSources
)
1112 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1113 lowest
= entry
.mSource
;
1115 if(lowest
&& lowest
->getPriority() < maxprio
)
1119 mMessage
->sourceForceStopped(lowest
);
1122 if(mSourceIds
.empty())
1123 throw std::runtime_error("No available sources");
1125 id
= mSourceIds
.top();
1131 Source
ContextImpl::createSource()
1136 if(!mFreeSources
.empty())
1138 source
= mFreeSources
.front();
1143 mAllSources
.emplace_back(this);
1144 source
= &mAllSources
.back();
1146 return Source(source
);
1150 void ContextImpl::addPlayingSource(SourceImpl
*source
, ALuint id
)
1152 auto iter
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1153 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1154 { return lhs
.mSource
< rhs
; }
1156 if(iter
== mPlaySources
.end() || iter
->mSource
!= source
)
1157 mPlaySources
.insert(iter
, {source
,id
});
1160 void ContextImpl::addPlayingSource(SourceImpl
*source
)
1162 auto iter
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1163 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1164 { return lhs
.mSource
< rhs
; }
1166 if(iter
== mStreamSources
.end() || iter
->mSource
!= source
)
1167 mStreamSources
.insert(iter
, {source
});
1170 void ContextImpl::removePlayingSource(SourceImpl
*source
)
1172 auto iter0
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1173 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1174 { return lhs
.mSource
< rhs
; }
1176 if(iter0
!= mPlaySources
.end() && iter0
->mSource
== source
)
1177 mPlaySources
.erase(iter0
);
1180 auto iter1
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1181 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1182 { return lhs
.mSource
< rhs
; }
1184 if(iter1
!= mStreamSources
.end() && iter1
->mSource
== source
)
1185 mStreamSources
.erase(iter1
);
1190 void ContextImpl::addStream(SourceImpl
*source
)
1192 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1193 if(mThread
.get_id() == std::thread::id())
1194 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
1195 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1196 if(iter
== mStreamingSources
.end() || *iter
!= source
)
1197 mStreamingSources
.insert(iter
, source
);
1200 void ContextImpl::removeStream(SourceImpl
*source
)
1202 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1203 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1204 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1205 mStreamingSources
.erase(iter
);
1208 void ContextImpl::removeStreamNoLock(SourceImpl
*source
)
1210 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1211 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1212 mStreamingSources
.erase(iter
);
1216 AuxiliaryEffectSlot
ContextImpl::createAuxiliaryEffectSlot()
1218 if(!hasExtension(EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
1219 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1224 alGenAuxiliaryEffectSlots(1, &id
);
1225 if(alGetError() != AL_NO_ERROR
)
1226 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1228 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id
));
1231 alDeleteAuxiliaryEffectSlots(1, &id
);
1237 Effect
ContextImpl::createEffect()
1239 if(!hasExtension(EXT_EFX
))
1240 throw std::runtime_error("Effects not supported");
1245 alGenEffects(1, &id
);
1246 if(alGetError() != AL_NO_ERROR
)
1247 throw std::runtime_error("Failed to create Effect");
1249 return Effect(new EffectImpl(this, id
));
1252 alDeleteEffects(1, &id
);
1258 SourceGroup
ContextImpl::createSourceGroup(StringView name
)
1260 auto hasher
= std::hash
<StringView
>();
1261 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), hasher(name
),
1262 [hasher
](const UniquePtr
<SourceGroupImpl
> &lhs
, size_t rhs
) -> bool
1263 { return hasher(lhs
->getName()) < rhs
; }
1265 if(iter
!= mSourceGroups
.end() && (*iter
)->getName() == name
)
1266 throw std::runtime_error("Duplicate source group name");
1267 iter
= mSourceGroups
.insert(iter
, MakeUnique
<SourceGroupImpl
>(this, String(name
)));
1268 return SourceGroup(iter
->get());
1271 SourceGroup
ContextImpl::getSourceGroup(StringView name
)
1273 auto hasher
= std::hash
<StringView
>();
1274 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), hasher(name
),
1275 [hasher
](const UniquePtr
<SourceGroupImpl
> &lhs
, size_t rhs
) -> bool
1276 { return hasher(lhs
->getName()) < rhs
; }
1278 if(iter
== mSourceGroups
.end() || (*iter
)->getName() != name
)
1279 throw std::runtime_error("Source group not found");
1280 return SourceGroup(iter
->get());
1283 void ContextImpl::freeSourceGroup(SourceGroupImpl
*group
)
1285 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), group
->getName(),
1286 [](const UniquePtr
<SourceGroupImpl
> &lhs
, const String
&rhs
) -> bool
1287 { return lhs
->getName() < rhs
; }
1289 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1290 mSourceGroups
.erase(iter
);
1294 void ContextImpl::setDopplerFactor(ALfloat factor
)
1296 if(!(factor
>= 0.0f
))
1297 throw std::runtime_error("Doppler factor out of range");
1299 alDopplerFactor(factor
);
1303 void ContextImpl::setSpeedOfSound(ALfloat speed
)
1306 throw std::runtime_error("Speed of sound out of range");
1308 alSpeedOfSound(speed
);
1312 void ContextImpl::setDistanceModel(DistanceModel model
)
1315 alDistanceModel((ALenum
)model
);
1319 void ContextImpl::update()
1323 std::remove_if(mPlaySources
.begin(), mPlaySources
.end(),
1324 [](const SourceBufferUpdateEntry
&entry
) -> bool
1325 { return !entry
.mSource
->playUpdate(entry
.mId
); }
1326 ), mPlaySources
.end()
1328 mStreamSources
.erase(
1329 std::remove_if(mStreamSources
.begin(), mStreamSources
.end(),
1330 [](const SourceStreamUpdateEntry
&entry
) -> bool
1331 { return !entry
.mSource
->playUpdate(); }
1332 ), mStreamSources
.end()
1334 if(!mWakeInterval
.load(std::memory_order_relaxed
).count())
1336 // For performance reasons, don't wait for the thread's mutex. This
1337 // should be called often enough to keep up with any and all streams
1339 mWakeThread
.notify_all();
1342 if(hasExtension(EXT_disconnect
) && mIsConnected
)
1345 alcGetIntegerv(alcGetContextsDevice(mContext
), ALC_CONNECTED
, 1, &connected
);
1346 mIsConnected
= connected
;
1347 if(!connected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(mDevice
));
1351 void Context::destroy()
1356 DECL_THUNK0(Device
, Context
, getDevice
,)
1357 DECL_THUNK0(void, Context
, startBatch
,)
1358 DECL_THUNK0(void, Context
, endBatch
,)
1359 DECL_THUNK0(Listener
, Context
, getListener
,)
1360 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
1361 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1362 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
1363 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1364 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, StringView
)
1365 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
1366 DECL_THUNK0(ArrayView
<String
>, Context
, getAvailableResamplers
,)
1367 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
1368 DECL_THUNK1(Buffer
, Context
, getBuffer
,, StringView
)
1369 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, getBufferAsync
,, StringView
)
1370 DECL_THUNK1(void, Context
, precacheBuffersAsync
,, ArrayView
<StringView
>)
1371 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, StringView
, SharedPtr
<Decoder
>)
1372 DECL_THUNK2(SharedFuture
<Buffer
>, Context
, createBufferAsyncFrom
,, StringView
, SharedPtr
<Decoder
>)
1373 DECL_THUNK1(void, Context
, removeBuffer
,, StringView
)
1374 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1375 DECL_THUNK0(Source
, Context
, createSource
,)
1376 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1377 DECL_THUNK0(Effect
, Context
, createEffect
,)
1378 DECL_THUNK1(SourceGroup
, Context
, createSourceGroup
,, StringView
)
1379 DECL_THUNK1(SourceGroup
, Context
, getSourceGroup
,, StringView
)
1380 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1381 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1382 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1383 DECL_THUNK0(void, Context
, update
,)
1386 void Context::MakeCurrent(Context context
)
1387 { ContextImpl::MakeCurrent(context
.pImpl
); }
1389 Context
Context::GetCurrent()
1390 { return Context(ContextImpl::GetCurrent()); }
1392 void Context::MakeThreadCurrent(Context context
)
1393 { ContextImpl::MakeThreadCurrent(context
.pImpl
); }
1395 Context
Context::GetThreadCurrent()
1396 { return Context(ContextImpl::GetThreadCurrent()); }
1399 void ListenerImpl::setGain(ALfloat gain
)
1402 throw std::runtime_error("Gain out of range");
1403 CheckContext(mContext
);
1404 alListenerf(AL_GAIN
, gain
);
1408 void ListenerImpl::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, std::pair
<Vector3
,Vector3
> orientation
)
1410 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1411 CheckContext(mContext
);
1412 Batcher batcher
= mContext
->getBatcher();
1413 alListenerfv(AL_POSITION
, position
.getPtr());
1414 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1415 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1418 void ListenerImpl::setPosition(ALfloat x
, ALfloat y
, ALfloat z
)
1420 CheckContext(mContext
);
1421 alListener3f(AL_POSITION
, x
, y
, z
);
1424 void ListenerImpl::setPosition(const ALfloat
*pos
)
1426 CheckContext(mContext
);
1427 alListenerfv(AL_POSITION
, pos
);
1430 void ListenerImpl::setVelocity(ALfloat x
, ALfloat y
, ALfloat z
)
1432 CheckContext(mContext
);
1433 alListener3f(AL_VELOCITY
, x
, y
, z
);
1436 void ListenerImpl::setVelocity(const ALfloat
*vel
)
1438 CheckContext(mContext
);
1439 alListenerfv(AL_VELOCITY
, vel
);
1442 void ListenerImpl::setOrientation(ALfloat x1
, ALfloat y1
, ALfloat z1
, ALfloat x2
, ALfloat y2
, ALfloat z2
)
1444 CheckContext(mContext
);
1445 ALfloat ori
[6] = { x1
, y1
, z1
, x2
, y2
, z2
};
1446 alListenerfv(AL_ORIENTATION
, ori
);
1449 void ListenerImpl::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1451 CheckContext(mContext
);
1452 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1453 alListenerfv(AL_ORIENTATION
, ori
);
1456 void ListenerImpl::setOrientation(const ALfloat
*ori
)
1458 CheckContext(mContext
);
1459 alListenerfv(AL_ORIENTATION
, ori
);
1462 void ListenerImpl::setMetersPerUnit(ALfloat m_u
)
1465 throw std::runtime_error("Invalid meters per unit");
1466 CheckContext(mContext
);
1467 if(mContext
->hasExtension(EXT_EFX
))
1468 alListenerf(AL_METERS_PER_UNIT
, m_u
);
1472 using Vector3Pair
= std::pair
<Vector3
,Vector3
>;
1474 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1475 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, Vector3Pair
)
1476 DECL_THUNK3(void, Listener
, setPosition
,, ALfloat
, ALfloat
, ALfloat
)
1477 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1478 DECL_THUNK3(void, Listener
, setVelocity
,, ALfloat
, ALfloat
, ALfloat
)
1479 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1480 DECL_THUNK6(void, Listener
, setOrientation
,, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
, ALfloat
)
1481 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1482 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1483 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)