Use bitfields and scoped enums for extensions
[alure.git] / src / context.cpp
blobfe449005b4e4231a70cb5f76a1dd4124bb7567f9
2 #include "config.h"
4 #include "context.h"
6 #include <stdexcept>
7 #include <algorithm>
8 #include <functional>
9 #include <memory>
10 #include <iostream>
11 #include <sstream>
12 #include <fstream>
13 #include <cstring>
14 #include <map>
15 #include <new>
17 #include "alc.h"
19 #ifdef HAVE_VORBISFILE
20 #include "decoders/vorbisfile.hpp"
21 #endif
22 #ifdef HAVE_LIBFLAC
23 #include "decoders/flac.hpp"
24 #endif
25 #ifdef HAVE_OPUSFILE
26 #include "decoders/opusfile.hpp"
27 #endif
28 #ifdef HAVE_LIBSNDFILE
29 #include "decoders/sndfile.hpp"
30 #endif
31 #ifdef HAVE_MPG123
32 #include "decoders/mpg123.hpp"
33 #endif
34 #include "decoders/wave.hpp"
36 #include "devicemanager.h"
37 #include "device.h"
38 #include "buffer.h"
39 #include "source.h"
40 #include "auxeffectslot.h"
41 #include "effect.h"
42 #include "sourcegroup.h"
44 #ifdef _WIN32
45 #define WIN32_LEAN_AND_MEAN
46 #include <windows.h>
47 #endif
49 namespace std
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.
58 template<>
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;
70 for(auto ch : str)
71 val = (val^traits_type::to_int_type(ch)) * hash_prime;
72 return val;
74 else
76 static constexpr size_t hash_offset = 0x811c9dc5;
77 static constexpr size_t hash_prime = 0x1000193;
79 size_t val = hash_offset;
80 for(auto ch : str)
81 val = (val^traits_type::to_int_type(ch)) * hash_prime;
82 return val;
89 namespace
92 // Global mutex to protect global context changes
93 std::mutex mGlobalCtxMutex;
95 #ifdef _WIN32
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;
103 HANDLE mFile;
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
110 DWORD got = 0;
111 if(!ReadFile(mFile, mBuffer.data(), mBuffer.size(), &got, NULL))
112 got = 0;
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();
125 LARGE_INTEGER fpos;
126 switch(whence)
128 case std::ios_base::beg:
129 fpos.QuadPart = offset;
130 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
131 return traits_type::eof();
132 break;
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
141 // offset.
142 fpos.QuadPart = 0;
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();
154 break;
156 case std::ios_base::end:
157 fpos.QuadPart = offset;
158 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
159 return traits_type::eof();
160 break;
162 default:
163 return traits_type::eof();
165 setg(0, 0, 0);
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();
175 LARGE_INTEGER fpos;
176 fpos.QuadPart = pos;
177 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
178 return traits_type::eof();
180 setg(0, 0, 0);
181 return fpos.QuadPart;
184 public:
185 bool open(const char *filename)
187 alure::Vector<wchar_t> wname;
188 int wnamelen;
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;
199 return true;
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)
209 CloseHandle(mFile);
210 mFile = INVALID_HANDLE_VALUE;
214 // Inherit from std::istream to use our custom streambuf
215 class Stream final : public std::istream {
216 public:
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)))
221 clear(failbit);
223 ~Stream() override
224 { delete rdbuf(); }
226 bool is_open() const { return static_cast<StreamBuf*>(rdbuf())->is_open(); }
228 #endif
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>() },
236 #endif
237 #ifdef HAVE_LIBFLAC
238 { "_alure_int_flac", alure::MakeUnique<alure::FlacDecoderFactory>() },
239 #endif
240 #ifdef HAVE_OPUSFILE
241 { "_alure_int_opus", alure::MakeUnique<alure::OpusFileDecoderFactory>() },
242 #endif
243 #ifdef HAVE_LIBSNDFILE
244 { "_alure_int_sndfile", alure::MakeUnique<alure::SndFileDecoderFactory>() },
245 #endif
246 #ifdef HAVE_MPG123
247 { "_alure_int_mpg123", alure::MakeUnique<alure::Mpg123DecoderFactory>() },
248 #endif
250 alure::Vector<DecoderEntryPair> sDecoders;
253 template<typename T>
254 alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> &file,
255 T start, T end)
257 alure::DecoderOrExceptT ret;
258 while(start != end)
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"));
267 ++start;
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
287 #ifdef _WIN32
288 auto file = alure::MakeUnique<Stream>(name.c_str());
289 #else
290 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
291 #endif
292 if(!file->is_open()) file = nullptr;
293 return std::move(file);
296 DefaultFileIOFactory sDefaultFileFactory;
298 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
302 namespace alure
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);
329 return factory;
331 return nullptr;
335 FileIOFactory::~FileIOFactory() { }
337 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
339 std::swap(sFileFactory, factory);
340 return 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)
374 return String();
378 template<typename T>
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 AL extension;
436 const char name[32];
437 void (&loader)(ContextImpl*);
438 } ALExtensionList[] = {
439 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
441 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
442 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
443 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
445 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
446 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
447 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
449 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
450 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
451 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
452 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
454 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
456 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
457 { AL::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->getALCcontext() : nullptr) == ALC_FALSE)
469 throw std::runtime_error("Call to alcMakeContextCurrent failed");
470 if(context)
472 context->addRef();
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)
484 ctxlock.unlock();
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->getALCcontext() : nullptr) == ALC_FALSE)
494 throw std::runtime_error("Call to alcSetThreadContext failed");
495 if(context)
497 context->addRef();
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->getALCdevice();
508 mHasExt.clear();
509 for(const auto &entry : ALExtensionList)
511 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
512 alIsExtensionPresent(entry.name))
514 mHasExt.set(static_cast<size_t>(entry.extension));
515 entry.loader(this);
521 void ContextImpl::backgroundProc()
523 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(ALC::EXT_thread_local_context))
524 DeviceManagerImpl::SetThreadContext(getALCcontext());
526 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
527 std::chrono::milliseconds waketime(0);
528 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
529 while(!mQuitThread.load(std::memory_order_acquire))
532 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
533 mStreamingSources.erase(
534 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
535 [](SourceImpl *source) -> bool
536 { return !source->updateAsync(); }
537 ), mStreamingSources.end()
541 // Only do one pending buffer at a time. In case there's several large
542 // buffers to load, we still need to process streaming sources so they
543 // don't underrun.
544 PendingBuffer *lastpb = mPendingCurrent.load(std::memory_order_acquire);
545 if(PendingBuffer *pb = lastpb->mNext.load(std::memory_order_relaxed))
547 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
548 pb->mPromise.set_value(Buffer(pb->mBuffer));
549 Promise<Buffer>().swap(pb->mPromise);
550 mPendingCurrent.store(pb, std::memory_order_release);
551 continue;
554 std::unique_lock<std::mutex> wakelock(mWakeMutex);
555 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
557 ctxlock.unlock();
559 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
560 if(interval.count() == 0)
561 mWakeThread.wait(wakelock);
562 else
564 auto now = std::chrono::steady_clock::now() - basetime;
565 if(now > waketime)
567 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
568 waketime += interval * mult;
570 mWakeThread.wait_until(wakelock, waketime + basetime);
572 wakelock.unlock();
574 ctxlock.lock();
575 while(!mQuitThread.load(std::memory_order_acquire) &&
576 alcGetCurrentContext() != getALCcontext())
577 mWakeThread.wait(ctxlock);
580 ctxlock.unlock();
582 if(DeviceManagerImpl::SetThreadContext)
583 DeviceManagerImpl::SetThreadContext(nullptr);
587 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
588 : mListener(this), mContext(context), mDevice(device),
589 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
590 mRefs(0), mIsConnected(true), mIsBatching(false),
591 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
592 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
593 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
594 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
595 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
596 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
597 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
598 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
599 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
600 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
602 mHasExt.clear();
603 mPendingHead = new PendingBuffer;
604 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
605 mPendingTail = mPendingHead;
608 ContextImpl::~ContextImpl()
610 PendingBuffer *pb = mPendingTail;
611 while(pb)
613 PendingBuffer *next = pb->mNext.load(std::memory_order_relaxed);
614 delete pb;
615 pb = next;
617 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
618 mPendingHead = nullptr;
619 mPendingTail = nullptr;
623 void ContextImpl::destroy()
625 if(mRefs != 0)
626 throw std::runtime_error("Context is in use");
627 if(!mBuffers.empty())
628 throw std::runtime_error("Trying to destroy a context with buffers");
630 if(mThread.joinable())
632 std::unique_lock<std::mutex> lock(mWakeMutex);
633 mQuitThread.store(true, std::memory_order_release);
634 lock.unlock();
635 mWakeThread.notify_all();
636 mThread.join();
639 alcDestroyContext(mContext);
640 mContext = nullptr;
642 mDevice->removeContext(this);
646 void ContextImpl::startBatch()
648 alcSuspendContext(mContext);
649 mIsBatching = true;
652 void ContextImpl::endBatch()
654 alcProcessContext(mContext);
655 mIsBatching = false;
659 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler> handler)
661 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
662 mMessage.swap(handler);
663 return handler;
667 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
669 if(interval.count() < 0 || interval > std::chrono::seconds(1))
670 throw std::runtime_error("Async wake interval out of range");
671 mWakeInterval.store(interval);
672 mWakeMutex.lock(); mWakeMutex.unlock();
673 mWakeThread.notify_all();
677 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
679 DecoderOrExceptT ret;
681 String oldname = String(name);
682 auto file = FileIOFactory::get().openFile(oldname);
683 if(file) return (ret = GetDecoder(name, std::move(file)));
685 // Resource not found. Try to find a substitute.
686 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+oldname));
687 do {
688 String newname(mMessage->resourceNotFound(oldname));
689 if(newname.empty())
690 return (ret = std::runtime_error("Failed to open "+oldname));
691 file = FileIOFactory::get().openFile(newname);
692 oldname = std::move(newname);
693 } while(!file);
695 return (ret = GetDecoder(oldname, std::move(file)));
698 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
700 CheckContext(this);
701 DecoderOrExceptT dec = findDecoder(name);
702 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
703 return *decoder;
704 throw std::get<std::runtime_error>(dec);
708 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
710 CheckContext(this);
711 return GetFormat(channels, type) != AL_NONE;
715 ArrayView<String> ContextImpl::getAvailableResamplers()
717 CheckContext(this);
718 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
720 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
721 mResamplers.reserve(num_resamplers);
722 for(int i = 0;i < num_resamplers;i++)
723 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
724 if(mResamplers.empty())
725 mResamplers.emplace_back();
727 return mResamplers;
730 ALsizei ContextImpl::getDefaultResamplerIndex() const
732 CheckContext(this);
733 if(!hasExtension(AL::SOFT_source_resampler))
734 return 0;
735 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
739 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
741 BufferOrExceptT retval;
742 ALuint srate = decoder->getFrequency();
743 ChannelConfig chans = decoder->getChannelConfig();
744 SampleType type = decoder->getSampleType();
745 ALuint frames = decoder->getLength();
747 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
748 frames = decoder->read(data.data(), frames);
749 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
750 data.resize(FramesToBytes(frames, chans, type));
752 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
753 if(loop_pts.first >= loop_pts.second)
754 loop_pts = std::make_pair(0, frames);
755 else
757 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
758 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
761 // Get the format before calling the bufferLoading message handler, to
762 // ensure it's something OpenAL can handle.
763 ALenum format = GetFormat(chans, type);
764 if(format == AL_NONE)
766 std::stringstream sstr;
767 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
768 return (retval = std::runtime_error(sstr.str()));
771 if(mMessage.get())
772 mMessage->bufferLoading(name, chans, type, srate, data);
774 alGetError();
775 ALuint bid = 0;
776 alGenBuffers(1, &bid);
777 alBufferData(bid, format, data.data(), data.size(), srate);
778 if(hasExtension(AL::SOFT_loop_points))
780 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
781 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
783 if(alGetError() != AL_NO_ERROR)
785 alDeleteBuffers(1, &bid);
786 return (retval = std::runtime_error("Failed to buffer data"));
789 return (retval = mBuffers.insert(iter,
790 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
791 )->get());
794 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
796 BufferOrExceptT retval;
797 ALuint srate = decoder->getFrequency();
798 ChannelConfig chans = decoder->getChannelConfig();
799 SampleType type = decoder->getSampleType();
800 ALuint frames = decoder->getLength();
801 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
803 ALenum format = GetFormat(chans, type);
804 if(format == AL_NONE)
806 std::stringstream sstr;
807 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
808 return (retval = std::runtime_error(sstr.str()));
811 alGetError();
812 ALuint bid = 0;
813 alGenBuffers(1, &bid);
814 if(alGetError() != AL_NO_ERROR)
815 return (retval = std::runtime_error("Failed to create buffer"));
817 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
819 if(mThread.get_id() == std::thread::id())
820 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
822 PendingBuffer *pb = nullptr;
823 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
824 pb = new PendingBuffer{buffer.get(), decoder, format, frames, std::move(promise)};
825 else
827 pb = mPendingTail;
828 pb->mBuffer = buffer.get();
829 pb->mDecoder = decoder;
830 pb->mFormat = format;
831 pb->mFrames = frames;
832 pb->mPromise = std::move(promise);
833 mPendingTail = pb->mNext.exchange(nullptr, std::memory_order_relaxed);
836 mPendingHead->mNext.store(pb, std::memory_order_release);
837 mPendingHead = pb;
839 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
842 Buffer ContextImpl::getBuffer(StringView name)
844 CheckContext(this);
846 auto hasher = std::hash<StringView>();
847 if(Expect<false>(!mFutureBuffers.empty()))
849 Buffer buffer;
851 // If the buffer is already pending for the future, wait for it
852 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
853 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
854 { return hasher(lhs.mBuffer->getName()) < rhs; }
856 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
858 buffer = iter->mFuture.get();
859 mFutureBuffers.erase(iter);
862 // Clear out any completed futures.
863 mFutureBuffers.erase(
864 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
865 [](const PendingFuture &entry) -> bool
866 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
867 ), mFutureBuffers.end()
870 // If we got the buffer, return it. Otherwise, go load it normally.
871 if(buffer) return buffer;
874 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
875 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
876 { return hasher(lhs->getName()) < rhs; }
878 if(iter != mBuffers.end() && (*iter)->getName() == name)
879 return Buffer(iter->get());
881 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
882 Buffer *buffer = std::get_if<Buffer>(&ret);
883 if(Expect<false>(!buffer))
884 throw std::get<std::runtime_error>(ret);
885 return *buffer;
888 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
890 SharedFuture<Buffer> future;
891 CheckContext(this);
893 auto hasher = std::hash<StringView>();
894 if(Expect<false>(!mFutureBuffers.empty()))
896 // Check if the future that's being created already exists
897 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
898 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
899 { return hasher(lhs.mBuffer->getName()) < rhs; }
901 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
903 future = iter->mFuture;
904 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
905 mFutureBuffers.erase(iter);
906 return future;
909 // Clear out any fulfilled futures.
910 mFutureBuffers.erase(
911 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
912 [](const PendingFuture &entry) -> bool
913 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
914 ), mFutureBuffers.end()
918 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
919 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
920 { return hasher(lhs->getName()) < rhs; }
922 if(iter != mBuffers.end() && (*iter)->getName() == name)
924 // User asked to create a future buffer that's already loaded. Just
925 // construct a promise, fulfill the promise immediately, then return a
926 // shared future that's already set.
927 Promise<Buffer> promise;
928 promise.set_value(Buffer(iter->get()));
929 return promise.get_future().share();
932 Promise<Buffer> promise;
933 future = promise.get_future().share();
935 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
936 Buffer *buffer = std::get_if<Buffer>(&ret);
937 if(Expect<false>(!buffer))
938 throw std::get<std::runtime_error>(ret);
939 mWakeMutex.lock(); mWakeMutex.unlock();
940 mWakeThread.notify_all();
942 mFutureBuffers.insert(
943 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
944 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
945 { return hasher(lhs.mBuffer->getName()) < rhs; }
946 ), { buffer->getHandle(), future }
949 return future;
952 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
954 CheckContext(this);
956 if(Expect<false>(!mFutureBuffers.empty()))
958 // Clear out any fulfilled futures.
959 mFutureBuffers.erase(
960 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
961 [](const PendingFuture &entry) -> bool
962 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
963 ), mFutureBuffers.end()
967 auto hasher = std::hash<StringView>();
968 for(const StringView name : names)
970 // Check if the buffer that's being created already exists
971 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
972 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
973 { return hasher(lhs->getName()) < rhs; }
975 if(iter != mBuffers.end() && (*iter)->getName() == name)
976 continue;
978 DecoderOrExceptT dec = findDecoder(name);
979 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
980 if(!decoder) continue;
982 Promise<Buffer> promise;
983 SharedFuture<Buffer> future = promise.get_future().share();
985 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
986 std::move(promise));
987 Buffer *buffer = std::get_if<Buffer>(&buf);
988 if(Expect<false>(!buffer)) continue;
990 mFutureBuffers.insert(
991 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
992 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
993 { return hasher(lhs.mBuffer->getName()) < rhs; }
994 ), { buffer->getHandle(), future }
997 mWakeMutex.lock(); mWakeMutex.unlock();
998 mWakeThread.notify_all();
1001 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder> decoder)
1003 CheckContext(this);
1005 auto hasher = std::hash<StringView>();
1006 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1007 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1008 { return hasher(lhs->getName()) < rhs; }
1010 if(iter != mBuffers.end() && (*iter)->getName() == name)
1011 throw std::runtime_error("Buffer \""+name+"\" already exists");
1013 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1014 Buffer *buffer = std::get_if<Buffer>(&ret);
1015 if(Expect<false>(!buffer))
1016 throw std::get<std::runtime_error>(ret);
1017 return *buffer;
1020 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder> decoder)
1022 SharedFuture<Buffer> future;
1023 CheckContext(this);
1025 if(Expect<false>(!mFutureBuffers.empty()))
1027 // Clear out any fulfilled futures.
1028 mFutureBuffers.erase(
1029 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1030 [](const PendingFuture &entry) -> bool
1031 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1032 ), mFutureBuffers.end()
1036 auto hasher = std::hash<StringView>();
1037 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1038 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1039 { return hasher(lhs->getName()) < rhs; }
1041 if(iter != mBuffers.end() && (*iter)->getName() == name)
1042 throw std::runtime_error("Buffer \""+name+"\" already exists");
1044 Promise<Buffer> promise;
1045 future = promise.get_future().share();
1047 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1048 Buffer *buffer = std::get_if<Buffer>(&ret);
1049 if(Expect<false>(!buffer))
1050 throw std::get<std::runtime_error>(ret);
1051 mWakeMutex.lock(); mWakeMutex.unlock();
1052 mWakeThread.notify_all();
1054 mFutureBuffers.insert(
1055 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1056 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1057 { return hasher(lhs.mBuffer->getName()) < rhs; }
1058 ), { buffer->getHandle(), future }
1061 return future;
1065 void ContextImpl::removeBuffer(StringView name)
1067 CheckContext(this);
1069 auto hasher = std::hash<StringView>();
1070 if(Expect<false>(!mFutureBuffers.empty()))
1072 // If the buffer is already pending for the future, wait for it to
1073 // finish before continuing.
1074 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1075 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1076 { return hasher(lhs.mBuffer->getName()) < rhs; }
1078 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1080 iter->mFuture.wait();
1081 mFutureBuffers.erase(iter);
1084 // Clear out any completed futures.
1085 mFutureBuffers.erase(
1086 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1087 [](const PendingFuture &entry) -> bool
1088 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1089 ), mFutureBuffers.end()
1093 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1094 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1095 { return hasher(lhs->getName()) < rhs; }
1097 if(iter != mBuffers.end() && (*iter)->getName() == name)
1099 // Remove pending sources whose future was waiting for this buffer.
1100 mPendingSources.erase(
1101 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1102 [iter](PendingSource &entry) -> bool
1104 return (entry.mFuture.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready &&
1105 entry.mFuture.get().getHandle() == iter->get());
1107 ), mPendingSources.end()
1109 (*iter)->cleanup();
1110 mBuffers.erase(iter);
1115 ALuint ContextImpl::getSourceId(ALuint maxprio)
1117 ALuint id = 0;
1118 if(mSourceIds.empty())
1120 alGetError();
1121 alGenSources(1, &id);
1122 if(alGetError() == AL_NO_ERROR)
1123 return id;
1125 SourceImpl *lowest = nullptr;
1126 for(SourceBufferUpdateEntry &entry : mPlaySources)
1128 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1129 lowest = entry.mSource;
1131 for(SourceStreamUpdateEntry &entry : mStreamSources)
1133 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1134 lowest = entry.mSource;
1136 if(lowest && lowest->getPriority() < maxprio)
1138 lowest->stop();
1139 if(mMessage.get())
1140 mMessage->sourceForceStopped(lowest);
1143 if(mSourceIds.empty())
1144 throw std::runtime_error("No available sources");
1146 id = mSourceIds.top();
1147 mSourceIds.pop();
1148 return id;
1152 Source ContextImpl::createSource()
1154 CheckContext(this);
1156 SourceImpl *source;
1157 if(!mFreeSources.empty())
1159 source = mFreeSources.back();
1160 mFreeSources.pop_back();
1162 else
1164 mAllSources.emplace_back(this);
1165 source = &mAllSources.back();
1167 return Source(source);
1171 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1173 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1174 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1175 { return lhs.mSource < rhs; }
1177 if(iter == mPendingSources.end() || iter->mSource != source)
1178 mPendingSources.insert(iter, {source, std::move(future)});
1181 void ContextImpl::removePendingSource(SourceImpl *source)
1183 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1184 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1185 { return lhs.mSource < rhs; }
1187 if(iter != mPendingSources.end() && iter->mSource == source)
1188 mPendingSources.erase(iter);
1191 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1193 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1194 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1195 { return lhs.mSource < rhs; }
1197 return (iter != mPendingSources.end() && iter->mSource == source);
1200 void ContextImpl::addFadingSource(SourceImpl *source)
1202 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1203 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1204 { return lhs < rhs; }
1206 if(iter == mFadingSources.end() || *iter != source)
1207 mFadingSources.insert(iter, source);
1210 void ContextImpl::removeFadingSource(SourceImpl *source)
1212 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1213 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1214 { return lhs < rhs; }
1216 if(iter != mFadingSources.end() && *iter == source)
1217 mFadingSources.erase(iter);
1220 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1222 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1223 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1224 { return lhs.mSource < rhs; }
1226 if(iter == mPlaySources.end() || iter->mSource != source)
1227 mPlaySources.insert(iter, {source,id});
1230 void ContextImpl::addPlayingSource(SourceImpl *source)
1232 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1233 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1234 { return lhs.mSource < rhs; }
1236 if(iter == mStreamSources.end() || iter->mSource != source)
1237 mStreamSources.insert(iter, {source});
1240 void ContextImpl::removePlayingSource(SourceImpl *source)
1242 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1243 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1244 { return lhs.mSource < rhs; }
1246 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1247 mPlaySources.erase(iter0);
1248 else
1250 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1251 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1252 { return lhs.mSource < rhs; }
1254 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1255 mStreamSources.erase(iter1);
1260 void ContextImpl::addStream(SourceImpl *source)
1262 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1263 if(mThread.get_id() == std::thread::id())
1264 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1265 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1266 if(iter == mStreamingSources.end() || *iter != source)
1267 mStreamingSources.insert(iter, source);
1270 void ContextImpl::removeStream(SourceImpl *source)
1272 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1273 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1274 if(iter != mStreamingSources.end() && *iter == source)
1275 mStreamingSources.erase(iter);
1278 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1280 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1281 if(iter != mStreamingSources.end() && *iter == source)
1282 mStreamingSources.erase(iter);
1286 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1288 if(!hasExtension(AL::EXT_EFX) || !alGenAuxiliaryEffectSlots)
1289 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1290 CheckContext(this);
1292 alGetError();
1293 ALuint id = 0;
1294 alGenAuxiliaryEffectSlots(1, &id);
1295 if(alGetError() != AL_NO_ERROR)
1296 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1297 try {
1298 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1300 catch(...) {
1301 alDeleteAuxiliaryEffectSlots(1, &id);
1302 throw;
1307 Effect ContextImpl::createEffect()
1309 if(!hasExtension(AL::EXT_EFX))
1310 throw std::runtime_error("Effects not supported");
1311 CheckContext(this);
1313 alGetError();
1314 ALuint id = 0;
1315 alGenEffects(1, &id);
1316 if(alGetError() != AL_NO_ERROR)
1317 throw std::runtime_error("Failed to create Effect");
1318 try {
1319 return Effect(new EffectImpl(this, id));
1321 catch(...) {
1322 alDeleteEffects(1, &id);
1323 throw;
1328 SourceGroup ContextImpl::createSourceGroup(StringView name)
1330 auto hasher = std::hash<StringView>();
1331 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1332 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1333 { return hasher(lhs->getName()) < rhs; }
1335 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1336 throw std::runtime_error("Duplicate source group name");
1337 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, String(name)));
1338 return SourceGroup(iter->get());
1341 SourceGroup ContextImpl::getSourceGroup(StringView name)
1343 auto hasher = std::hash<StringView>();
1344 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1345 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1346 { return hasher(lhs->getName()) < rhs; }
1348 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1349 throw std::runtime_error("Source group not found");
1350 return SourceGroup(iter->get());
1353 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1355 auto hasher = std::hash<StringView>();
1356 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(group->getName()),
1357 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1358 { return hasher(lhs->getName()) < rhs; }
1360 if(iter != mSourceGroups.end() && iter->get() == group)
1361 mSourceGroups.erase(iter);
1365 void ContextImpl::setDopplerFactor(ALfloat factor)
1367 if(!(factor >= 0.0f))
1368 throw std::runtime_error("Doppler factor out of range");
1369 CheckContext(this);
1370 alDopplerFactor(factor);
1374 void ContextImpl::setSpeedOfSound(ALfloat speed)
1376 if(!(speed > 0.0f))
1377 throw std::runtime_error("Speed of sound out of range");
1378 CheckContext(this);
1379 alSpeedOfSound(speed);
1383 void ContextImpl::setDistanceModel(DistanceModel model)
1385 CheckContext(this);
1386 alDistanceModel((ALenum)model);
1390 void ContextImpl::update()
1392 CheckContext(this);
1393 mPendingSources.erase(
1394 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1395 [](PendingSource &entry) -> bool
1396 { return !entry.mSource->checkPending(entry.mFuture); }
1397 ), mPendingSources.end()
1399 if(!mFadingSources.empty())
1401 auto cur_time = std::chrono::steady_clock::now();
1402 mFadingSources.erase(
1403 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1404 [cur_time](SourceImpl *source) -> bool
1405 { return !source->fadeUpdate(cur_time); }
1406 ), mFadingSources.end()
1409 mPlaySources.erase(
1410 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1411 [](const SourceBufferUpdateEntry &entry) -> bool
1412 { return !entry.mSource->playUpdate(entry.mId); }
1413 ), mPlaySources.end()
1415 mStreamSources.erase(
1416 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1417 [](const SourceStreamUpdateEntry &entry) -> bool
1418 { return !entry.mSource->playUpdate(); }
1419 ), mStreamSources.end()
1422 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1424 // For performance reasons, don't wait for the thread's mutex. This
1425 // should be called often enough to keep up with any and all streams
1426 // regardless.
1427 mWakeThread.notify_all();
1430 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1432 ALCint connected;
1433 alcGetIntegerv(mDevice->getALCdevice(), ALC_CONNECTED, 1, &connected);
1434 mIsConnected = connected;
1435 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1439 void Context::destroy()
1441 pImpl->destroy();
1442 pImpl = nullptr;
1444 DECL_THUNK0(Device, Context, getDevice,)
1445 DECL_THUNK0(void, Context, startBatch,)
1446 DECL_THUNK0(void, Context, endBatch,)
1447 DECL_THUNK0(Listener, Context, getListener,)
1448 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1449 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1450 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1451 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1452 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
1453 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1454 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1455 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1456 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1457 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1458 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1459 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1460 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1461 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1462 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1463 DECL_THUNK0(Source, Context, createSource,)
1464 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1465 DECL_THUNK0(Effect, Context, createEffect,)
1466 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, StringView)
1467 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, StringView)
1468 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1469 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1470 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1471 DECL_THUNK0(void, Context, update,)
1474 void Context::MakeCurrent(Context context)
1475 { ContextImpl::MakeCurrent(context.pImpl); }
1477 Context Context::GetCurrent()
1478 { return Context(ContextImpl::GetCurrent()); }
1480 void Context::MakeThreadCurrent(Context context)
1481 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1483 Context Context::GetThreadCurrent()
1484 { return Context(ContextImpl::GetThreadCurrent()); }
1487 void ListenerImpl::setGain(ALfloat gain)
1489 if(!(gain >= 0.0f))
1490 throw std::runtime_error("Gain out of range");
1491 CheckContext(mContext);
1492 alListenerf(AL_GAIN, gain);
1496 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1498 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1499 CheckContext(mContext);
1500 Batcher batcher = mContext->getBatcher();
1501 alListenerfv(AL_POSITION, position.getPtr());
1502 alListenerfv(AL_VELOCITY, velocity.getPtr());
1503 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1506 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1508 CheckContext(mContext);
1509 alListener3f(AL_POSITION, x, y, z);
1512 void ListenerImpl::setPosition(const ALfloat *pos)
1514 CheckContext(mContext);
1515 alListenerfv(AL_POSITION, pos);
1518 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1520 CheckContext(mContext);
1521 alListener3f(AL_VELOCITY, x, y, z);
1524 void ListenerImpl::setVelocity(const ALfloat *vel)
1526 CheckContext(mContext);
1527 alListenerfv(AL_VELOCITY, vel);
1530 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1532 CheckContext(mContext);
1533 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1534 alListenerfv(AL_ORIENTATION, ori);
1537 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1539 CheckContext(mContext);
1540 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1541 alListenerfv(AL_ORIENTATION, ori);
1544 void ListenerImpl::setOrientation(const ALfloat *ori)
1546 CheckContext(mContext);
1547 alListenerfv(AL_ORIENTATION, ori);
1550 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1552 if(!(m_u > 0.0f))
1553 throw std::runtime_error("Invalid meters per unit");
1554 CheckContext(mContext);
1555 if(mContext->hasExtension(AL::EXT_EFX))
1556 alListenerf(AL_METERS_PER_UNIT, m_u);
1560 using Vector3Pair = std::pair<Vector3,Vector3>;
1562 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1563 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1564 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1565 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1566 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1567 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1568 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1569 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1570 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1571 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)