Throw more appropriate exception types
[alure.git] / src / context.cpp
blob093969664eb1b6e8258bf32c3f8ae34dd04ec507
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::UniquePtr<std::istream> &file, T start, T end)
256 alure::DecoderOrExceptT ret;
257 while(start != end)
259 alure::DecoderFactory *factory = start->second.get();
260 auto decoder = factory->createDecoder(file);
261 if(decoder) return (ret = std::move(decoder));
263 if(!file || !(file->clear(),file->seekg(0)))
264 return (ret = std::runtime_error("Failed to rewind file for the next decoder factory"));
266 ++start;
269 return (ret = nullptr);
272 static alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> file)
274 auto decoder = GetDecoder(file, sDecoders.begin(), sDecoders.end());
275 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
276 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
277 decoder = GetDecoder(file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
278 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
279 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
280 return (decoder = std::runtime_error("No decoder found"));
283 class DefaultFileIOFactory final : public alure::FileIOFactory {
284 alure::UniquePtr<std::istream> openFile(const alure::String &name) override
286 #ifdef _WIN32
287 auto file = alure::MakeUnique<Stream>(name.c_str());
288 #else
289 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
290 #endif
291 if(!file->is_open()) file = nullptr;
292 return std::move(file);
295 DefaultFileIOFactory sDefaultFileFactory;
297 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
301 namespace alure
304 using Vector3Pair = std::pair<Vector3,Vector3>;
307 Decoder::~Decoder() { }
308 DecoderFactory::~DecoderFactory() { }
310 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
312 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
313 [](const DecoderEntryPair &entry, StringView rhs) -> bool
314 { return entry.first < rhs; }
316 if(iter != sDecoders.end())
317 throw std::runtime_error("Decoder factory already registered");
318 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
321 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name)
323 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
324 [](const DecoderEntryPair &entry, StringView rhs) -> bool
325 { return entry.first < rhs; }
327 if(iter != sDecoders.end())
329 UniquePtr<DecoderFactory> factory = std::move(iter->second);
330 sDecoders.erase(iter);
331 return factory;
333 return nullptr;
337 FileIOFactory::~FileIOFactory() { }
339 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
341 std::swap(sFileFactory, factory);
342 return factory;
345 FileIOFactory &FileIOFactory::get()
347 FileIOFactory *factory = sFileFactory.get();
348 if(factory) return *factory;
349 return sDefaultFileFactory;
353 // Default message handler methods are no-ops.
354 MessageHandler::~MessageHandler()
358 void MessageHandler::deviceDisconnected(Device)
362 void MessageHandler::sourceStopped(Source)
366 void MessageHandler::sourceForceStopped(Source)
370 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>)
374 String MessageHandler::resourceNotFound(StringView)
376 return String();
380 template<typename T>
381 static inline void LoadALFunc(T **func, const char *name)
382 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
384 static void LoadNothing(ContextImpl*) { }
386 static void LoadEFX(ContextImpl *ctx)
388 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
389 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
390 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
391 LoadALFunc(&ctx->alEffecti, "alEffecti");
392 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
393 LoadALFunc(&ctx->alEffectf, "alEffectf");
394 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
395 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
396 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
397 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
398 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
400 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
401 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
402 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
403 LoadALFunc(&ctx->alFilteri, "alFilteri");
404 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
405 LoadALFunc(&ctx->alFilterf, "alFilterf");
406 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
407 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
408 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
409 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
410 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
412 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
413 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
414 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
415 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
416 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
417 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
418 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
419 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
420 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
421 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
422 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
425 static void LoadSourceResampler(ContextImpl *ctx)
427 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
430 static void LoadSourceLatency(ContextImpl *ctx)
432 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
433 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
436 static const struct {
437 enum AL extension;
438 const char name[32];
439 void (&loader)(ContextImpl*);
440 } ALExtensionList[] = {
441 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
443 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
444 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
445 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
447 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
448 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
449 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
451 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
452 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
453 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
454 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
456 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
458 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
459 { AL::EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
463 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
464 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
466 std::atomic<uint64_t> ContextImpl::sContextSetCount{0};
468 void ContextImpl::MakeCurrent(ContextImpl *context)
470 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
472 if(alcMakeContextCurrent(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
473 throw std::runtime_error("Call to alcMakeContextCurrent failed");
474 if(context)
476 context->addRef();
477 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
479 std::swap(sCurrentCtx, context);
480 if(context) context->decRef();
482 if(sThreadCurrentCtx)
483 sThreadCurrentCtx->decRef();
484 sThreadCurrentCtx = nullptr;
485 sContextSetCount.fetch_add(1, std::memory_order_release);
487 if((context = sCurrentCtx) != nullptr)
489 ctxlock.unlock();
490 context->mWakeThread.notify_all();
494 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
496 if(!DeviceManagerImpl::SetThreadContext)
497 throw std::runtime_error("Thread-local contexts unsupported");
498 if(DeviceManagerImpl::SetThreadContext(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
499 throw std::runtime_error("Call to alcSetThreadContext failed");
500 if(context)
502 context->addRef();
503 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
505 if(sThreadCurrentCtx)
506 sThreadCurrentCtx->decRef();
507 sThreadCurrentCtx = context;
508 sContextSetCount.fetch_add(1, std::memory_order_release);
511 void ContextImpl::setupExts()
513 ALCdevice *device = mDevice->getALCdevice();
514 mHasExt.clear();
515 for(const auto &entry : ALExtensionList)
517 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
518 alIsExtensionPresent(entry.name))
520 mHasExt.set(static_cast<size_t>(entry.extension));
521 entry.loader(this);
527 void ContextImpl::backgroundProc()
529 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(ALC::EXT_thread_local_context))
530 DeviceManagerImpl::SetThreadContext(getALCcontext());
532 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
533 std::chrono::milliseconds waketime(0);
534 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
535 while(!mQuitThread.load(std::memory_order_acquire))
538 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
539 mStreamingSources.erase(
540 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
541 [](SourceImpl *source) -> bool
542 { return !source->updateAsync(); }
543 ), mStreamingSources.end()
547 // Only do one pending buffer at a time. In case there's several large
548 // buffers to load, we still need to process streaming sources so they
549 // don't underrun.
550 PendingPromise *lastpb = mPendingCurrent.load(std::memory_order_acquire);
551 if(PendingPromise *pb = lastpb->mNext.load(std::memory_order_relaxed))
553 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
554 pb->mPromise.set_value(Buffer(pb->mBuffer));
555 Promise<Buffer>().swap(pb->mPromise);
556 mPendingCurrent.store(pb, std::memory_order_release);
557 continue;
560 std::unique_lock<std::mutex> wakelock(mWakeMutex);
561 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
563 ctxlock.unlock();
565 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
566 if(interval.count() == 0)
567 mWakeThread.wait(wakelock);
568 else
570 auto now = std::chrono::steady_clock::now() - basetime;
571 if(now > waketime)
573 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
574 waketime += interval * mult;
576 mWakeThread.wait_until(wakelock, waketime + basetime);
578 wakelock.unlock();
580 ctxlock.lock();
581 while(!mQuitThread.load(std::memory_order_acquire) &&
582 alcGetCurrentContext() != getALCcontext())
583 mWakeThread.wait(ctxlock);
586 ctxlock.unlock();
588 if(DeviceManagerImpl::SetThreadContext)
589 DeviceManagerImpl::SetThreadContext(nullptr);
593 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
594 : mContextSetCounter(std::numeric_limits<uint64_t>::max()),
595 mListener(this), mContext(context), mDevice(device),
596 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
597 mRefs(0), mIsConnected(true), mIsBatching(false),
598 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
599 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
600 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
601 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
602 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
603 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
604 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
605 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
606 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
607 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
609 mHasExt.clear();
610 mSourceIds.reserve(256);
611 mPendingHead = new PendingPromise;
612 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
613 mPendingTail = mPendingHead;
616 ContextImpl::~ContextImpl()
618 PendingPromise *pb = mPendingTail;
619 while(pb)
621 PendingPromise *next = pb->mNext.load(std::memory_order_relaxed);
622 delete pb;
623 pb = next;
625 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
626 mPendingHead = nullptr;
627 mPendingTail = nullptr;
631 void Context::destroy()
633 ContextImpl *i = pImpl;
634 pImpl = nullptr;
635 i->destroy();
637 void ContextImpl::destroy()
639 if(mRefs != 0)
640 throw std::runtime_error("Context is in use");
641 if(!mBuffers.empty())
642 throw std::runtime_error("Trying to destroy a context with buffers");
644 if(mThread.joinable())
646 std::unique_lock<std::mutex> lock(mWakeMutex);
647 mQuitThread.store(true, std::memory_order_release);
648 lock.unlock();
649 mWakeThread.notify_all();
650 mThread.join();
653 alcDestroyContext(mContext);
654 mContext = nullptr;
656 mDevice->removeContext(this);
660 DECL_THUNK0(void, Context, startBatch,)
661 void ContextImpl::startBatch()
663 alcSuspendContext(mContext);
664 mIsBatching = true;
667 DECL_THUNK0(void, Context, endBatch,)
668 void ContextImpl::endBatch()
670 alcProcessContext(mContext);
671 mIsBatching = false;
675 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
676 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler>&& handler)
678 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
679 mMessage.swap(handler);
680 return handler;
684 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
685 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
687 if(interval.count() < 0 || interval > std::chrono::seconds(1))
688 throw std::out_of_range("Async wake interval out of range");
689 mWakeInterval.store(interval);
690 mWakeMutex.lock(); mWakeMutex.unlock();
691 mWakeThread.notify_all();
695 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
697 DecoderOrExceptT ret;
699 String oldname = String(name);
700 auto file = FileIOFactory::get().openFile(oldname);
701 if(file) return (ret = GetDecoder(std::move(file)));
703 // Resource not found. Try to find a substitute.
704 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open file"));
705 do {
706 String newname(mMessage->resourceNotFound(oldname));
707 if(newname.empty())
708 return (ret = std::runtime_error("Failed to open file"));
709 file = FileIOFactory::get().openFile(newname);
710 oldname = std::move(newname);
711 } while(!file);
713 return (ret = GetDecoder(std::move(file)));
716 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
717 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
719 CheckContext(this);
720 DecoderOrExceptT dec = findDecoder(name);
721 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
722 return *decoder;
723 throw std::get<std::runtime_error>(dec);
727 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
728 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
730 CheckContext(this);
731 return GetFormat(channels, type) != AL_NONE;
735 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
736 ArrayView<String> ContextImpl::getAvailableResamplers()
738 CheckContext(this);
739 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
741 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
742 mResamplers.reserve(num_resamplers);
743 for(int i = 0;i < num_resamplers;i++)
744 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
745 if(mResamplers.empty())
746 mResamplers.emplace_back();
748 return mResamplers;
751 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
752 ALsizei ContextImpl::getDefaultResamplerIndex() const
754 CheckContext(this);
755 if(!hasExtension(AL::SOFT_source_resampler))
756 return 0;
757 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
761 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
763 BufferOrExceptT retval;
764 ALuint srate = decoder->getFrequency();
765 ChannelConfig chans = decoder->getChannelConfig();
766 SampleType type = decoder->getSampleType();
767 ALuint frames = decoder->getLength();
769 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
770 frames = decoder->read(data.data(), frames);
771 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
772 data.resize(FramesToBytes(frames, chans, type));
774 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
775 if(loop_pts.first >= loop_pts.second)
776 loop_pts = std::make_pair(0, frames);
777 else
779 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
780 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
783 // Get the format before calling the bufferLoading message handler, to
784 // ensure it's something OpenAL can handle.
785 ALenum format = GetFormat(chans, type);
786 if(format == AL_NONE)
788 String str("Unsupported format (");
789 str += GetSampleTypeName(type);
790 str += ", ";
791 str += GetChannelConfigName(chans);
792 str += ")";
793 return (retval = std::runtime_error(str));
796 if(mMessage.get())
797 mMessage->bufferLoading(name, chans, type, srate, data);
799 alGetError();
800 ALuint bid = 0;
801 alGenBuffers(1, &bid);
802 alBufferData(bid, format, data.data(), data.size(), srate);
803 if(hasExtension(AL::SOFT_loop_points))
805 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
806 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
808 if(alGetError() != AL_NO_ERROR)
810 alDeleteBuffers(1, &bid);
811 return (retval = std::runtime_error("Failed to buffer data"));
814 return (retval = mBuffers.insert(iter,
815 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
816 )->get());
819 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
821 BufferOrExceptT retval;
822 ALuint srate = decoder->getFrequency();
823 ChannelConfig chans = decoder->getChannelConfig();
824 SampleType type = decoder->getSampleType();
825 ALuint frames = decoder->getLength();
826 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
828 ALenum format = GetFormat(chans, type);
829 if(format == AL_NONE)
831 std::stringstream sstr;
832 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
833 return (retval = std::runtime_error(sstr.str()));
836 alGetError();
837 ALuint bid = 0;
838 alGenBuffers(1, &bid);
839 if(alGetError() != AL_NO_ERROR)
840 return (retval = std::runtime_error("Failed to create buffer"));
842 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
844 if(mThread.get_id() == std::thread::id())
845 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
847 PendingPromise *pf = nullptr;
848 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
849 pf = new PendingPromise{buffer.get(), decoder, format, frames, std::move(promise)};
850 else
852 pf = mPendingTail;
853 pf->mBuffer = buffer.get();
854 pf->mDecoder = decoder;
855 pf->mFormat = format;
856 pf->mFrames = frames;
857 pf->mPromise = std::move(promise);
858 mPendingTail = pf->mNext.exchange(nullptr, std::memory_order_relaxed);
861 mPendingHead->mNext.store(pf, std::memory_order_release);
862 mPendingHead = pf;
864 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
867 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
868 Buffer ContextImpl::getBuffer(StringView name)
870 CheckContext(this);
872 auto hasher = std::hash<StringView>();
873 if(UNLIKELY(!mFutureBuffers.empty()))
875 Buffer buffer;
877 // If the buffer is already pending for the future, wait for it
878 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
879 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
880 { return hasher(lhs.mBuffer->getName()) < rhs; }
882 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
884 buffer = iter->mFuture.get();
885 mFutureBuffers.erase(iter);
888 // Clear out any completed futures.
889 mFutureBuffers.erase(
890 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
891 [](const PendingBuffer &entry) -> bool
892 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
893 ), mFutureBuffers.end()
896 // If we got the buffer, return it. Otherwise, go load it normally.
897 if(buffer) return buffer;
900 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
901 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
902 { return hasher(lhs->getName()) < rhs; }
904 if(iter != mBuffers.end() && (*iter)->getName() == name)
905 return Buffer(iter->get());
907 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
908 Buffer *buffer = std::get_if<Buffer>(&ret);
909 if(UNLIKELY(!buffer))
910 throw std::get<std::runtime_error>(ret);
911 return *buffer;
914 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
915 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
917 SharedFuture<Buffer> future;
918 CheckContext(this);
920 auto hasher = std::hash<StringView>();
921 if(UNLIKELY(!mFutureBuffers.empty()))
923 // Check if the future that's being created already exists
924 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
925 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
926 { return hasher(lhs.mBuffer->getName()) < rhs; }
928 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
930 future = iter->mFuture;
931 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
932 mFutureBuffers.erase(iter);
933 return future;
936 // Clear out any fulfilled futures.
937 mFutureBuffers.erase(
938 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
939 [](const PendingBuffer &entry) -> bool
940 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
941 ), mFutureBuffers.end()
945 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
946 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
947 { return hasher(lhs->getName()) < rhs; }
949 if(iter != mBuffers.end() && (*iter)->getName() == name)
951 // User asked to create a future buffer that's already loaded. Just
952 // construct a promise, fulfill the promise immediately, then return a
953 // shared future that's already set.
954 Promise<Buffer> promise;
955 promise.set_value(Buffer(iter->get()));
956 future = promise.get_future().share();
957 return future;
960 Promise<Buffer> promise;
961 future = promise.get_future().share();
963 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
964 Buffer *buffer = std::get_if<Buffer>(&ret);
965 if(UNLIKELY(!buffer))
966 throw std::get<std::runtime_error>(ret);
967 mWakeMutex.lock(); mWakeMutex.unlock();
968 mWakeThread.notify_all();
970 mFutureBuffers.insert(
971 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
972 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
973 { return hasher(lhs.mBuffer->getName()) < rhs; }
974 ), { buffer->getHandle(), future }
977 return future;
980 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
981 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
983 CheckContext(this);
985 if(UNLIKELY(!mFutureBuffers.empty()))
987 // Clear out any fulfilled futures.
988 mFutureBuffers.erase(
989 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
990 [](const PendingBuffer &entry) -> bool
991 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
992 ), mFutureBuffers.end()
996 auto hasher = std::hash<StringView>();
997 for(const StringView name : names)
999 // Check if the buffer that's being created already exists
1000 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1001 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1002 { return hasher(lhs->getName()) < rhs; }
1004 if(iter != mBuffers.end() && (*iter)->getName() == name)
1005 continue;
1007 DecoderOrExceptT dec = findDecoder(name);
1008 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
1009 if(!decoder) continue;
1011 Promise<Buffer> promise;
1012 SharedFuture<Buffer> future = promise.get_future().share();
1014 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
1015 std::move(promise));
1016 Buffer *buffer = std::get_if<Buffer>(&buf);
1017 if(UNLIKELY(!buffer)) continue;
1019 mFutureBuffers.insert(
1020 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1021 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1022 { return hasher(lhs.mBuffer->getName()) < rhs; }
1023 ), { buffer->getHandle(), future }
1026 mWakeMutex.lock(); mWakeMutex.unlock();
1027 mWakeThread.notify_all();
1030 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1031 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder>&& decoder)
1033 CheckContext(this);
1035 auto hasher = std::hash<StringView>();
1036 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1037 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1038 { return hasher(lhs->getName()) < rhs; }
1040 if(iter != mBuffers.end() && (*iter)->getName() == name)
1041 throw std::runtime_error("Buffer already exists");
1043 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1044 Buffer *buffer = std::get_if<Buffer>(&ret);
1045 if(UNLIKELY(!buffer))
1046 throw std::get<std::runtime_error>(ret);
1047 return *buffer;
1050 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1051 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder>&& decoder)
1053 SharedFuture<Buffer> future;
1054 CheckContext(this);
1056 if(UNLIKELY(!mFutureBuffers.empty()))
1058 // Clear out any fulfilled futures.
1059 mFutureBuffers.erase(
1060 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1061 [](const PendingBuffer &entry) -> bool
1062 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1063 ), mFutureBuffers.end()
1067 auto hasher = std::hash<StringView>();
1068 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1069 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1070 { return hasher(lhs->getName()) < rhs; }
1072 if(iter != mBuffers.end() && (*iter)->getName() == name)
1073 throw std::runtime_error("Buffer already exists");
1075 Promise<Buffer> promise;
1076 future = promise.get_future().share();
1078 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1079 Buffer *buffer = std::get_if<Buffer>(&ret);
1080 if(UNLIKELY(!buffer))
1081 throw std::get<std::runtime_error>(ret);
1082 mWakeMutex.lock(); mWakeMutex.unlock();
1083 mWakeThread.notify_all();
1085 mFutureBuffers.insert(
1086 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1087 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1088 { return hasher(lhs.mBuffer->getName()) < rhs; }
1089 ), { buffer->getHandle(), future }
1092 return future;
1096 DECL_THUNK1(Buffer, Context, findBuffer,, StringView)
1097 Buffer ContextImpl::findBuffer(StringView name)
1099 Buffer buffer;
1100 CheckContext(this);
1102 auto hasher = std::hash<StringView>();
1103 if(UNLIKELY(!mFutureBuffers.empty()))
1105 // If the buffer is already pending for the future, wait for it
1106 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1107 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1108 { return hasher(lhs.mBuffer->getName()) < rhs; }
1110 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1112 buffer = iter->mFuture.get();
1113 mFutureBuffers.erase(iter);
1116 // Clear out any completed futures.
1117 mFutureBuffers.erase(
1118 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1119 [](const PendingBuffer &entry) -> bool
1120 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1121 ), mFutureBuffers.end()
1125 if(LIKELY(!buffer))
1127 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1128 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1129 { return hasher(lhs->getName()) < rhs; }
1131 if(iter != mBuffers.end() && (*iter)->getName() == name)
1132 buffer = Buffer(iter->get());
1134 return buffer;
1137 DECL_THUNK1(SharedFuture<Buffer>, Context, findBufferAsync,, StringView)
1138 SharedFuture<Buffer> ContextImpl::findBufferAsync(StringView name)
1140 SharedFuture<Buffer> future;
1141 CheckContext(this);
1143 auto hasher = std::hash<StringView>();
1144 if(UNLIKELY(!mFutureBuffers.empty()))
1146 // Check if the future that's being created already exists
1147 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1148 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1149 { return hasher(lhs.mBuffer->getName()) < rhs; }
1151 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1153 future = iter->mFuture;
1154 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
1155 mFutureBuffers.erase(iter);
1156 return future;
1159 // Clear out any fulfilled futures.
1160 mFutureBuffers.erase(
1161 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1162 [](const PendingBuffer &entry) -> bool
1163 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1164 ), mFutureBuffers.end()
1168 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1169 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1170 { return hasher(lhs->getName()) < rhs; }
1172 if(iter != mBuffers.end() && (*iter)->getName() == name)
1174 // User asked to create a future buffer that's already loaded. Just
1175 // construct a promise, fulfill the promise immediately, then return a
1176 // shared future that's already set.
1177 Promise<Buffer> promise;
1178 promise.set_value(Buffer(iter->get()));
1179 future = promise.get_future().share();
1181 return future;
1185 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1186 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1187 void ContextImpl::removeBuffer(StringView name)
1189 CheckContext(this);
1191 auto hasher = std::hash<StringView>();
1192 if(UNLIKELY(!mFutureBuffers.empty()))
1194 // If the buffer is already pending for the future, wait for it to
1195 // finish before continuing.
1196 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1197 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1198 { return hasher(lhs.mBuffer->getName()) < rhs; }
1200 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1202 iter->mFuture.wait();
1203 mFutureBuffers.erase(iter);
1206 // Clear out any completed futures.
1207 mFutureBuffers.erase(
1208 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1209 [](const PendingBuffer &entry) -> bool
1210 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1211 ), mFutureBuffers.end()
1215 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1216 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1217 { return hasher(lhs->getName()) < rhs; }
1219 if(iter != mBuffers.end() && (*iter)->getName() == name)
1221 // Remove pending sources whose future was waiting for this buffer.
1222 mPendingSources.erase(
1223 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1224 [iter](PendingSource &entry) -> bool
1226 return (entry.mFuture.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready &&
1227 entry.mFuture.get().getHandle() == iter->get());
1229 ), mPendingSources.end()
1231 (*iter)->cleanup();
1232 mBuffers.erase(iter);
1237 ALuint ContextImpl::getSourceId(ALuint maxprio)
1239 ALuint id = 0;
1240 if(mSourceIds.empty())
1242 alGetError();
1243 alGenSources(1, &id);
1244 if(alGetError() == AL_NO_ERROR)
1245 return id;
1247 SourceImpl *lowest = nullptr;
1248 for(SourceBufferUpdateEntry &entry : mPlaySources)
1250 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1251 lowest = entry.mSource;
1253 for(SourceStreamUpdateEntry &entry : mStreamSources)
1255 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1256 lowest = entry.mSource;
1258 if(lowest && lowest->getPriority() < maxprio)
1260 lowest->stop();
1261 if(mMessage.get())
1262 mMessage->sourceForceStopped(lowest);
1265 if(mSourceIds.empty())
1266 throw std::runtime_error("No available sources");
1268 id = mSourceIds.back();
1269 mSourceIds.pop_back();
1270 return id;
1274 DECL_THUNK0(Source, Context, createSource,)
1275 Source ContextImpl::createSource()
1277 CheckContext(this);
1279 SourceImpl *source;
1280 if(!mFreeSources.empty())
1282 source = mFreeSources.back();
1283 mFreeSources.pop_back();
1285 else
1287 mAllSources.emplace_back(this);
1288 source = &mAllSources.back();
1290 return Source(source);
1294 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1296 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1297 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1298 { return lhs.mSource < rhs; }
1300 if(iter == mPendingSources.end() || iter->mSource != source)
1301 mPendingSources.insert(iter, {source, std::move(future)});
1304 void ContextImpl::removePendingSource(SourceImpl *source)
1306 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1307 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1308 { return lhs.mSource < rhs; }
1310 if(iter != mPendingSources.end() && iter->mSource == source)
1311 mPendingSources.erase(iter);
1314 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1316 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1317 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1318 { return lhs.mSource < rhs; }
1320 return (iter != mPendingSources.end() && iter->mSource == source);
1323 void ContextImpl::addFadingSource(SourceImpl *source)
1325 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1326 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1327 { return lhs < rhs; }
1329 if(iter == mFadingSources.end() || *iter != source)
1330 mFadingSources.insert(iter, source);
1333 void ContextImpl::removeFadingSource(SourceImpl *source)
1335 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1336 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1337 { return lhs < rhs; }
1339 if(iter != mFadingSources.end() && *iter == source)
1340 mFadingSources.erase(iter);
1343 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1345 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1346 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1347 { return lhs.mSource < rhs; }
1349 if(iter == mPlaySources.end() || iter->mSource != source)
1350 mPlaySources.insert(iter, {source,id});
1353 void ContextImpl::addPlayingSource(SourceImpl *source)
1355 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1356 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1357 { return lhs.mSource < rhs; }
1359 if(iter == mStreamSources.end() || iter->mSource != source)
1360 mStreamSources.insert(iter, {source});
1363 void ContextImpl::removePlayingSource(SourceImpl *source)
1365 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1366 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1367 { return lhs.mSource < rhs; }
1369 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1370 mPlaySources.erase(iter0);
1371 else
1373 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1374 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1375 { return lhs.mSource < rhs; }
1377 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1378 mStreamSources.erase(iter1);
1383 void ContextImpl::addStream(SourceImpl *source)
1385 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1386 if(mThread.get_id() == std::thread::id())
1387 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1388 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1389 if(iter == mStreamingSources.end() || *iter != source)
1390 mStreamingSources.insert(iter, source);
1393 void ContextImpl::removeStream(SourceImpl *source)
1395 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1396 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1397 if(iter != mStreamingSources.end() && *iter == source)
1398 mStreamingSources.erase(iter);
1401 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1403 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1404 if(iter != mStreamingSources.end() && *iter == source)
1405 mStreamingSources.erase(iter);
1409 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1410 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1412 if(!hasExtension(AL::EXT_EFX) || !alGenAuxiliaryEffectSlots)
1413 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1414 CheckContext(this);
1416 alGetError();
1417 ALuint id = 0;
1418 alGenAuxiliaryEffectSlots(1, &id);
1419 if(alGetError() != AL_NO_ERROR)
1420 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1421 try {
1422 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1424 catch(...) {
1425 alDeleteAuxiliaryEffectSlots(1, &id);
1426 throw;
1431 DECL_THUNK0(Effect, Context, createEffect,)
1432 Effect ContextImpl::createEffect()
1434 if(!hasExtension(AL::EXT_EFX))
1435 throw std::runtime_error("Effects not supported");
1436 CheckContext(this);
1438 alGetError();
1439 ALuint id = 0;
1440 alGenEffects(1, &id);
1441 if(alGetError() != AL_NO_ERROR)
1442 throw std::runtime_error("Failed to create Effect");
1443 try {
1444 return Effect(new EffectImpl(this, id));
1446 catch(...) {
1447 alDeleteEffects(1, &id);
1448 throw;
1453 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, StringView)
1454 SourceGroup ContextImpl::createSourceGroup(StringView name)
1456 auto hasher = std::hash<StringView>();
1457 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1458 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1459 { return hasher(lhs->getName()) < rhs; }
1461 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1462 throw std::runtime_error("Duplicate source group name");
1463 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, name));
1464 return SourceGroup(iter->get());
1467 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, StringView)
1468 SourceGroup ContextImpl::getSourceGroup(StringView name)
1470 auto hasher = std::hash<StringView>();
1471 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1472 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1473 { return hasher(lhs->getName()) < rhs; }
1475 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1476 throw std::runtime_error("Source group not found");
1477 return SourceGroup(iter->get());
1480 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1482 auto hasher = std::hash<StringView>();
1483 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(group->getName()),
1484 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1485 { return hasher(lhs->getName()) < rhs; }
1487 if(iter != mSourceGroups.end() && iter->get() == group)
1488 mSourceGroups.erase(iter);
1492 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1493 void ContextImpl::setDopplerFactor(ALfloat factor)
1495 if(!(factor >= 0.0f))
1496 throw std::out_of_range("Doppler factor out of range");
1497 CheckContext(this);
1498 alDopplerFactor(factor);
1502 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1503 void ContextImpl::setSpeedOfSound(ALfloat speed)
1505 if(!(speed > 0.0f))
1506 throw std::out_of_range("Speed of sound out of range");
1507 CheckContext(this);
1508 alSpeedOfSound(speed);
1512 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1513 void ContextImpl::setDistanceModel(DistanceModel model)
1515 CheckContext(this);
1516 alDistanceModel((ALenum)model);
1520 DECL_THUNK0(void, Context, update,)
1521 void ContextImpl::update()
1523 CheckContext(this);
1524 mPendingSources.erase(
1525 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1526 [](PendingSource &entry) -> bool
1527 { return !entry.mSource->checkPending(entry.mFuture); }
1528 ), mPendingSources.end()
1530 if(!mFadingSources.empty())
1532 auto cur_time = std::chrono::steady_clock::now();
1533 mFadingSources.erase(
1534 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1535 [cur_time](SourceImpl *source) -> bool
1536 { return !source->fadeUpdate(cur_time); }
1537 ), mFadingSources.end()
1540 mPlaySources.erase(
1541 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1542 [](const SourceBufferUpdateEntry &entry) -> bool
1543 { return !entry.mSource->playUpdate(entry.mId); }
1544 ), mPlaySources.end()
1546 mStreamSources.erase(
1547 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1548 [](const SourceStreamUpdateEntry &entry) -> bool
1549 { return !entry.mSource->playUpdate(); }
1550 ), mStreamSources.end()
1553 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1555 // For performance reasons, don't wait for the thread's mutex. This
1556 // should be called often enough to keep up with any and all streams
1557 // regardless.
1558 mWakeThread.notify_all();
1561 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1563 ALCint connected;
1564 alcGetIntegerv(mDevice->getALCdevice(), ALC_CONNECTED, 1, &connected);
1565 mIsConnected = connected;
1566 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1570 DECL_THUNK0(Device, Context, getDevice,)
1571 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1572 DECL_THUNK0(Listener, Context, getListener,)
1573 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1575 void Context::MakeCurrent(Context context)
1576 { ContextImpl::MakeCurrent(context.pImpl); }
1578 Context Context::GetCurrent()
1579 { return Context(ContextImpl::GetCurrent()); }
1581 void Context::MakeThreadCurrent(Context context)
1582 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1584 Context Context::GetThreadCurrent()
1585 { return Context(ContextImpl::GetThreadCurrent()); }
1588 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1589 void ListenerImpl::setGain(ALfloat gain)
1591 if(!(gain >= 0.0f))
1592 throw std::out_of_range("Gain out of range");
1593 CheckContext(mContext);
1594 alListenerf(AL_GAIN, gain);
1598 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1599 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1601 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1602 CheckContext(mContext);
1603 Batcher batcher = mContext->getBatcher();
1604 alListenerfv(AL_POSITION, position.getPtr());
1605 alListenerfv(AL_VELOCITY, velocity.getPtr());
1606 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1609 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1610 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1612 CheckContext(mContext);
1613 alListener3f(AL_POSITION, x, y, z);
1616 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1617 void ListenerImpl::setPosition(const ALfloat *pos)
1619 CheckContext(mContext);
1620 alListenerfv(AL_POSITION, pos);
1623 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1624 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1626 CheckContext(mContext);
1627 alListener3f(AL_VELOCITY, x, y, z);
1630 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1631 void ListenerImpl::setVelocity(const ALfloat *vel)
1633 CheckContext(mContext);
1634 alListenerfv(AL_VELOCITY, vel);
1637 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1638 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1640 CheckContext(mContext);
1641 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1642 alListenerfv(AL_ORIENTATION, ori);
1645 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1646 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1648 CheckContext(mContext);
1649 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1650 alListenerfv(AL_ORIENTATION, ori);
1653 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1654 void ListenerImpl::setOrientation(const ALfloat *ori)
1656 CheckContext(mContext);
1657 alListenerfv(AL_ORIENTATION, ori);
1660 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)
1661 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1663 if(!(m_u > 0.0f))
1664 throw std::out_of_range("Invalid meters per unit");
1665 CheckContext(mContext);
1666 if(mContext->hasExtension(AL::EXT_EFX))
1667 alListenerf(AL_METERS_PER_UNIT, m_u);