Add an inline helper to get a SharedFuture's state
[alure.git] / src / context.cpp
blob46967b7596da64822dc3fdf3b5e3198b9dd08bca
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::make_exception_ptr(std::runtime_error(
265 "Failed to rewind file for the next decoder factory"
266 )));
268 ++start;
271 return (ret = alure::SharedPtr<alure::Decoder>(nullptr));
274 static alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> file)
276 auto decoder = GetDecoder(file, sDecoders.begin(), sDecoders.end());
277 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
278 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
279 decoder = GetDecoder(file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
280 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
281 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
282 return (decoder = std::make_exception_ptr(std::runtime_error("No decoder found")));
285 class DefaultFileIOFactory final : public alure::FileIOFactory {
286 alure::UniquePtr<std::istream> openFile(const alure::String &name) override
288 #ifdef _WIN32
289 auto file = alure::MakeUnique<Stream>(name.c_str());
290 #else
291 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
292 #endif
293 if(!file->is_open()) file = nullptr;
294 return std::move(file);
297 DefaultFileIOFactory sDefaultFileFactory;
299 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
303 namespace alure
306 using Vector3Pair = std::pair<Vector3,Vector3>;
309 Decoder::~Decoder() { }
310 DecoderFactory::~DecoderFactory() { }
312 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
314 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
315 [](const DecoderEntryPair &entry, StringView rhs) -> bool
316 { return entry.first < rhs; }
318 if(iter != sDecoders.end())
319 throw std::runtime_error("Decoder factory already registered");
320 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
323 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name)
325 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
326 [](const DecoderEntryPair &entry, StringView rhs) -> bool
327 { return entry.first < rhs; }
329 if(iter != sDecoders.end())
331 UniquePtr<DecoderFactory> factory = std::move(iter->second);
332 sDecoders.erase(iter);
333 return factory;
335 return nullptr;
339 FileIOFactory::~FileIOFactory() { }
341 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
343 std::swap(sFileFactory, factory);
344 return factory;
347 FileIOFactory &FileIOFactory::get()
349 FileIOFactory *factory = sFileFactory.get();
350 if(factory) return *factory;
351 return sDefaultFileFactory;
355 // Default message handler methods are no-ops.
356 MessageHandler::~MessageHandler()
360 void MessageHandler::deviceDisconnected(Device)
364 void MessageHandler::sourceStopped(Source)
368 void MessageHandler::sourceForceStopped(Source)
372 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>)
376 String MessageHandler::resourceNotFound(StringView)
378 return String();
382 template<typename T>
383 static inline void LoadALFunc(T **func, const char *name)
384 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
386 static void LoadNothing(ContextImpl*) { }
388 static void LoadEFX(ContextImpl *ctx)
390 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
391 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
392 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
393 LoadALFunc(&ctx->alEffecti, "alEffecti");
394 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
395 LoadALFunc(&ctx->alEffectf, "alEffectf");
396 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
397 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
398 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
399 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
400 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
402 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
403 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
404 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
405 LoadALFunc(&ctx->alFilteri, "alFilteri");
406 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
407 LoadALFunc(&ctx->alFilterf, "alFilterf");
408 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
409 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
410 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
411 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
412 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
414 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
415 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
416 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
417 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
418 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
419 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
420 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
421 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
422 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
423 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
424 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
427 static void LoadSourceResampler(ContextImpl *ctx)
429 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
432 static void LoadSourceLatency(ContextImpl *ctx)
434 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
435 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
438 static const struct {
439 enum AL extension;
440 const char name[32];
441 void (&loader)(ContextImpl*);
442 } ALExtensionList[] = {
443 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
445 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
446 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
447 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
449 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
450 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
451 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
453 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
454 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
455 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
456 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
458 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
460 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
461 { AL::EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
465 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
466 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
468 std::atomic<uint64_t> ContextImpl::sContextSetCount{0};
470 void ContextImpl::MakeCurrent(ContextImpl *context)
472 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
474 if(alcMakeContextCurrent(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
475 throw std::runtime_error("Call to alcMakeContextCurrent failed");
476 if(context)
478 context->addRef();
479 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
481 std::swap(sCurrentCtx, context);
482 if(context) context->decRef();
484 if(sThreadCurrentCtx)
485 sThreadCurrentCtx->decRef();
486 sThreadCurrentCtx = nullptr;
487 sContextSetCount.fetch_add(1, std::memory_order_release);
489 if((context = sCurrentCtx) != nullptr)
491 ctxlock.unlock();
492 context->mWakeThread.notify_all();
496 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
498 if(!DeviceManagerImpl::SetThreadContext)
499 throw std::runtime_error("Thread-local contexts unsupported");
500 if(DeviceManagerImpl::SetThreadContext(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
501 throw std::runtime_error("Call to alcSetThreadContext failed");
502 if(context)
504 context->addRef();
505 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
507 if(sThreadCurrentCtx)
508 sThreadCurrentCtx->decRef();
509 sThreadCurrentCtx = context;
510 sContextSetCount.fetch_add(1, std::memory_order_release);
513 void ContextImpl::setupExts()
515 ALCdevice *device = mDevice->getALCdevice();
516 mHasExt.clear();
517 for(const auto &entry : ALExtensionList)
519 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
520 alIsExtensionPresent(entry.name))
522 mHasExt.set(static_cast<size_t>(entry.extension));
523 entry.loader(this);
529 void ContextImpl::backgroundProc()
531 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(ALC::EXT_thread_local_context))
532 DeviceManagerImpl::SetThreadContext(getALCcontext());
534 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
535 std::chrono::milliseconds waketime(0);
536 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
537 while(!mQuitThread.load(std::memory_order_acquire))
540 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
541 mStreamingSources.erase(
542 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
543 [](SourceImpl *source) -> bool
544 { return !source->updateAsync(); }
545 ), mStreamingSources.end()
549 // Only do one pending buffer at a time. In case there's several large
550 // buffers to load, we still need to process streaming sources so they
551 // don't underrun.
552 PendingPromise *lastpb = mPendingCurrent.load(std::memory_order_acquire);
553 if(PendingPromise *pb = lastpb->mNext.load(std::memory_order_relaxed))
555 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
556 pb->mPromise.set_value(Buffer(pb->mBuffer));
557 Promise<Buffer>().swap(pb->mPromise);
558 mPendingCurrent.store(pb, std::memory_order_release);
559 continue;
562 std::unique_lock<std::mutex> wakelock(mWakeMutex);
563 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
565 ctxlock.unlock();
567 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
568 if(interval.count() == 0)
569 mWakeThread.wait(wakelock);
570 else
572 auto now = std::chrono::steady_clock::now() - basetime;
573 if(now > waketime)
575 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
576 waketime += interval * mult;
578 mWakeThread.wait_until(wakelock, waketime + basetime);
580 wakelock.unlock();
582 ctxlock.lock();
583 while(!mQuitThread.load(std::memory_order_acquire) &&
584 alcGetCurrentContext() != getALCcontext())
585 mWakeThread.wait(ctxlock);
588 ctxlock.unlock();
590 if(DeviceManagerImpl::SetThreadContext)
591 DeviceManagerImpl::SetThreadContext(nullptr);
595 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
596 : mContextSetCounter(std::numeric_limits<uint64_t>::max()),
597 mListener(this), mContext(context), mDevice(device),
598 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
599 mRefs(0), mIsConnected(true), mIsBatching(false),
600 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
601 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
602 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
603 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
604 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
605 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
606 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
607 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
608 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
609 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
611 mHasExt.clear();
612 mSourceIds.reserve(256);
613 mPendingHead = new PendingPromise;
614 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
615 mPendingTail = mPendingHead;
618 ContextImpl::~ContextImpl()
620 PendingPromise *pb = mPendingTail;
621 while(pb)
623 PendingPromise *next = pb->mNext.load(std::memory_order_relaxed);
624 delete pb;
625 pb = next;
627 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
628 mPendingHead = nullptr;
629 mPendingTail = nullptr;
633 void Context::destroy()
635 ContextImpl *i = pImpl;
636 pImpl = nullptr;
637 i->destroy();
639 void ContextImpl::destroy()
641 if(mRefs != 0)
642 throw std::runtime_error("Context is in use");
643 if(!mBuffers.empty())
644 throw std::runtime_error("Trying to destroy a context with buffers");
646 if(mThread.joinable())
648 std::unique_lock<std::mutex> lock(mWakeMutex);
649 mQuitThread.store(true, std::memory_order_release);
650 lock.unlock();
651 mWakeThread.notify_all();
652 mThread.join();
655 alcDestroyContext(mContext);
656 mContext = nullptr;
658 mDevice->removeContext(this);
662 DECL_THUNK0(void, Context, startBatch,)
663 void ContextImpl::startBatch()
665 alcSuspendContext(mContext);
666 mIsBatching = true;
669 DECL_THUNK0(void, Context, endBatch,)
670 void ContextImpl::endBatch()
672 alcProcessContext(mContext);
673 mIsBatching = false;
677 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
678 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler>&& handler)
680 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
681 mMessage.swap(handler);
682 return handler;
686 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
687 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
689 if(interval.count() < 0 || interval > std::chrono::seconds(1))
690 throw std::out_of_range("Async wake interval out of range");
691 mWakeInterval.store(interval);
692 mWakeMutex.lock(); mWakeMutex.unlock();
693 mWakeThread.notify_all();
697 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
699 DecoderOrExceptT ret;
701 String oldname = String(name);
702 auto file = FileIOFactory::get().openFile(oldname);
703 if(file) return (ret = GetDecoder(std::move(file)));
705 // Resource not found. Try to find a substitute.
706 if(!mMessage.get())
707 return (ret = std::make_exception_ptr(std::runtime_error("Failed to open file")));
708 do {
709 String newname(mMessage->resourceNotFound(oldname));
710 if(newname.empty())
711 return (ret = std::make_exception_ptr(std::runtime_error("Failed to open file")));
712 file = FileIOFactory::get().openFile(newname);
713 oldname = std::move(newname);
714 } while(!file);
716 return (ret = GetDecoder(std::move(file)));
719 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
720 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
722 CheckContext(this);
723 DecoderOrExceptT dec = findDecoder(name);
724 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
725 return *decoder;
726 std::rethrow_exception(std::get<std::exception_ptr>(dec));
730 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
731 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
733 CheckContext(this);
734 return GetFormat(channels, type) != AL_NONE;
738 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
739 ArrayView<String> ContextImpl::getAvailableResamplers()
741 CheckContext(this);
742 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
744 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
745 mResamplers.reserve(num_resamplers);
746 for(int i = 0;i < num_resamplers;i++)
747 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
748 if(mResamplers.empty())
749 mResamplers.emplace_back();
751 return mResamplers;
754 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
755 ALsizei ContextImpl::getDefaultResamplerIndex() const
757 CheckContext(this);
758 if(!hasExtension(AL::SOFT_source_resampler))
759 return 0;
760 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
764 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
766 BufferOrExceptT retval;
767 ALuint srate = decoder->getFrequency();
768 ChannelConfig chans = decoder->getChannelConfig();
769 SampleType type = decoder->getSampleType();
770 ALuint frames = decoder->getLength();
772 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
773 frames = decoder->read(data.data(), frames);
774 if(!frames)
775 return (retval = std::make_exception_ptr(std::runtime_error("No samples for buffer")));
776 data.resize(FramesToBytes(frames, chans, type));
778 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
779 if(loop_pts.first >= loop_pts.second)
780 loop_pts = std::make_pair(0, frames);
781 else
783 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
784 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
787 // Get the format before calling the bufferLoading message handler, to
788 // ensure it's something OpenAL can handle.
789 ALenum format = GetFormat(chans, type);
790 if(format == AL_NONE)
792 String str("Unsupported format (");
793 str += GetSampleTypeName(type);
794 str += ", ";
795 str += GetChannelConfigName(chans);
796 str += ")";
797 return (retval = std::make_exception_ptr(std::runtime_error(str)));
800 if(mMessage.get())
801 mMessage->bufferLoading(name, chans, type, srate, data);
803 alGetError();
804 ALuint bid = 0;
805 alGenBuffers(1, &bid);
806 alBufferData(bid, format, data.data(), data.size(), srate);
807 if(hasExtension(AL::SOFT_loop_points))
809 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
810 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
812 ALenum err = alGetError();
813 if(err != AL_NO_ERROR)
815 alDeleteBuffers(1, &bid);
816 return (retval = std::make_exception_ptr(al_error(err, "Failed to buffer data")));
819 return (retval = mBuffers.insert(iter,
820 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
821 )->get());
824 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
826 BufferOrExceptT retval;
827 ALuint srate = decoder->getFrequency();
828 ChannelConfig chans = decoder->getChannelConfig();
829 SampleType type = decoder->getSampleType();
830 ALuint frames = decoder->getLength();
831 if(!frames)
832 return (retval = std::make_exception_ptr(std::runtime_error("No samples for buffer")));
834 ALenum format = GetFormat(chans, type);
835 if(format == AL_NONE)
837 std::stringstream sstr;
838 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
839 return (retval = std::make_exception_ptr(std::runtime_error(sstr.str())));
842 alGetError();
843 ALuint bid = 0;
844 alGenBuffers(1, &bid);
845 ALenum err = alGetError();
846 if(err != AL_NO_ERROR)
847 return (retval = std::make_exception_ptr(al_error(err, "Failed to create buffer")));
849 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
851 if(mThread.get_id() == std::thread::id())
852 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
854 PendingPromise *pf = nullptr;
855 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
856 pf = new PendingPromise{buffer.get(), decoder, format, frames, std::move(promise)};
857 else
859 pf = mPendingTail;
860 pf->mBuffer = buffer.get();
861 pf->mDecoder = decoder;
862 pf->mFormat = format;
863 pf->mFrames = frames;
864 pf->mPromise = std::move(promise);
865 mPendingTail = pf->mNext.exchange(nullptr, std::memory_order_relaxed);
868 mPendingHead->mNext.store(pf, std::memory_order_release);
869 mPendingHead = pf;
871 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
874 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
875 Buffer ContextImpl::getBuffer(StringView name)
877 CheckContext(this);
879 auto hasher = std::hash<StringView>();
880 if(UNLIKELY(!mFutureBuffers.empty()))
882 Buffer buffer;
884 // If the buffer is already pending for the future, wait for it
885 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
886 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
887 { return hasher(lhs.mBuffer->getName()) < rhs; }
889 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
891 buffer = iter->mFuture.get();
892 mFutureBuffers.erase(iter);
895 // Clear out any completed futures.
896 mFutureBuffers.erase(
897 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
898 [](const PendingBuffer &entry) -> bool
899 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
900 ), mFutureBuffers.end()
903 // If we got the buffer, return it. Otherwise, go load it normally.
904 if(buffer) return buffer;
907 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
908 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
909 { return hasher(lhs->getName()) < rhs; }
911 if(iter != mBuffers.end() && (*iter)->getName() == name)
912 return Buffer(iter->get());
914 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
915 Buffer *buffer = std::get_if<Buffer>(&ret);
916 if(UNLIKELY(!buffer))
917 std::rethrow_exception(std::get<std::exception_ptr>(ret));
918 return *buffer;
921 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
922 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
924 SharedFuture<Buffer> future;
925 CheckContext(this);
927 auto hasher = std::hash<StringView>();
928 if(UNLIKELY(!mFutureBuffers.empty()))
930 // Check if the future that's being created already exists
931 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
932 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
933 { return hasher(lhs.mBuffer->getName()) < rhs; }
935 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
937 future = iter->mFuture;
938 if(GetFutureState(future) == std::future_status::ready)
939 mFutureBuffers.erase(iter);
940 return future;
943 // Clear out any fulfilled futures.
944 mFutureBuffers.erase(
945 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
946 [](const PendingBuffer &entry) -> bool
947 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
948 ), mFutureBuffers.end()
952 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
953 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
954 { return hasher(lhs->getName()) < rhs; }
956 if(iter != mBuffers.end() && (*iter)->getName() == name)
958 // User asked to create a future buffer that's already loaded. Just
959 // construct a promise, fulfill the promise immediately, then return a
960 // shared future that's already set.
961 Promise<Buffer> promise;
962 promise.set_value(Buffer(iter->get()));
963 future = promise.get_future().share();
964 return future;
967 Promise<Buffer> promise;
968 future = promise.get_future().share();
970 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
971 Buffer *buffer = std::get_if<Buffer>(&ret);
972 if(UNLIKELY(!buffer))
973 std::rethrow_exception(std::get<std::exception_ptr>(ret));
974 mWakeMutex.lock(); mWakeMutex.unlock();
975 mWakeThread.notify_all();
977 mFutureBuffers.insert(
978 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
979 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
980 { return hasher(lhs.mBuffer->getName()) < rhs; }
981 ), { buffer->getHandle(), future }
984 return future;
987 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
988 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
990 CheckContext(this);
992 if(UNLIKELY(!mFutureBuffers.empty()))
994 // Clear out any fulfilled futures.
995 mFutureBuffers.erase(
996 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
997 [](const PendingBuffer &entry) -> bool
998 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
999 ), mFutureBuffers.end()
1003 auto hasher = std::hash<StringView>();
1004 for(const StringView name : names)
1006 // Check if the buffer that's being created already exists
1007 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1008 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1009 { return hasher(lhs->getName()) < rhs; }
1011 if(iter != mBuffers.end() && (*iter)->getName() == name)
1012 continue;
1014 DecoderOrExceptT dec = findDecoder(name);
1015 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
1016 if(!decoder) continue;
1018 Promise<Buffer> promise;
1019 SharedFuture<Buffer> future = promise.get_future().share();
1021 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
1022 std::move(promise));
1023 Buffer *buffer = std::get_if<Buffer>(&buf);
1024 if(UNLIKELY(!buffer)) continue;
1026 mFutureBuffers.insert(
1027 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1028 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1029 { return hasher(lhs.mBuffer->getName()) < rhs; }
1030 ), { buffer->getHandle(), future }
1033 mWakeMutex.lock(); mWakeMutex.unlock();
1034 mWakeThread.notify_all();
1037 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1038 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder>&& decoder)
1040 CheckContext(this);
1042 auto hasher = std::hash<StringView>();
1043 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1044 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1045 { return hasher(lhs->getName()) < rhs; }
1047 if(iter != mBuffers.end() && (*iter)->getName() == name)
1048 throw std::runtime_error("Buffer already exists");
1050 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1051 Buffer *buffer = std::get_if<Buffer>(&ret);
1052 if(UNLIKELY(!buffer))
1053 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1054 return *buffer;
1057 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1058 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder>&& decoder)
1060 SharedFuture<Buffer> future;
1061 CheckContext(this);
1063 if(UNLIKELY(!mFutureBuffers.empty()))
1065 // Clear out any fulfilled futures.
1066 mFutureBuffers.erase(
1067 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1068 [](const PendingBuffer &entry) -> bool
1069 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1070 ), mFutureBuffers.end()
1074 auto hasher = std::hash<StringView>();
1075 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1076 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1077 { return hasher(lhs->getName()) < rhs; }
1079 if(iter != mBuffers.end() && (*iter)->getName() == name)
1080 throw std::runtime_error("Buffer already exists");
1082 Promise<Buffer> promise;
1083 future = promise.get_future().share();
1085 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1086 Buffer *buffer = std::get_if<Buffer>(&ret);
1087 if(UNLIKELY(!buffer))
1088 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1089 mWakeMutex.lock(); mWakeMutex.unlock();
1090 mWakeThread.notify_all();
1092 mFutureBuffers.insert(
1093 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1094 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1095 { return hasher(lhs.mBuffer->getName()) < rhs; }
1096 ), { buffer->getHandle(), future }
1099 return future;
1103 DECL_THUNK1(Buffer, Context, findBuffer,, StringView)
1104 Buffer ContextImpl::findBuffer(StringView name)
1106 Buffer buffer;
1107 CheckContext(this);
1109 auto hasher = std::hash<StringView>();
1110 if(UNLIKELY(!mFutureBuffers.empty()))
1112 // If the buffer is already pending for the future, wait for it
1113 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1114 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1115 { return hasher(lhs.mBuffer->getName()) < rhs; }
1117 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1119 buffer = iter->mFuture.get();
1120 mFutureBuffers.erase(iter);
1123 // Clear out any completed futures.
1124 mFutureBuffers.erase(
1125 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1126 [](const PendingBuffer &entry) -> bool
1127 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1128 ), mFutureBuffers.end()
1132 if(LIKELY(!buffer))
1134 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1135 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1136 { return hasher(lhs->getName()) < rhs; }
1138 if(iter != mBuffers.end() && (*iter)->getName() == name)
1139 buffer = Buffer(iter->get());
1141 return buffer;
1144 DECL_THUNK1(SharedFuture<Buffer>, Context, findBufferAsync,, StringView)
1145 SharedFuture<Buffer> ContextImpl::findBufferAsync(StringView name)
1147 SharedFuture<Buffer> future;
1148 CheckContext(this);
1150 auto hasher = std::hash<StringView>();
1151 if(UNLIKELY(!mFutureBuffers.empty()))
1153 // Check if the future that's being created already exists
1154 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1155 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1156 { return hasher(lhs.mBuffer->getName()) < rhs; }
1158 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1160 future = iter->mFuture;
1161 if(GetFutureState(future) == std::future_status::ready)
1162 mFutureBuffers.erase(iter);
1163 return future;
1166 // Clear out any fulfilled futures.
1167 mFutureBuffers.erase(
1168 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1169 [](const PendingBuffer &entry) -> bool
1170 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1171 ), mFutureBuffers.end()
1175 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1176 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1177 { return hasher(lhs->getName()) < rhs; }
1179 if(iter != mBuffers.end() && (*iter)->getName() == name)
1181 // User asked to create a future buffer that's already loaded. Just
1182 // construct a promise, fulfill the promise immediately, then return a
1183 // shared future that's already set.
1184 Promise<Buffer> promise;
1185 promise.set_value(Buffer(iter->get()));
1186 future = promise.get_future().share();
1188 return future;
1192 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1193 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1194 void ContextImpl::removeBuffer(StringView name)
1196 CheckContext(this);
1198 auto hasher = std::hash<StringView>();
1199 if(UNLIKELY(!mFutureBuffers.empty()))
1201 // If the buffer is already pending for the future, wait for it to
1202 // finish before continuing.
1203 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1204 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1205 { return hasher(lhs.mBuffer->getName()) < rhs; }
1207 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1209 iter->mFuture.wait();
1210 mFutureBuffers.erase(iter);
1213 // Clear out any completed futures.
1214 mFutureBuffers.erase(
1215 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1216 [](const PendingBuffer &entry) -> bool
1217 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1218 ), mFutureBuffers.end()
1222 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1223 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1224 { return hasher(lhs->getName()) < rhs; }
1226 if(iter != mBuffers.end() && (*iter)->getName() == name)
1228 // Remove pending sources whose future was waiting for this buffer.
1229 mPendingSources.erase(
1230 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1231 [iter](PendingSource &entry) -> bool
1233 return (GetFutureState(entry.mFuture) == std::future_status::ready &&
1234 entry.mFuture.get().getHandle() == iter->get());
1236 ), mPendingSources.end()
1238 (*iter)->cleanup();
1239 mBuffers.erase(iter);
1244 ALuint ContextImpl::getSourceId(ALuint maxprio)
1246 ALuint id = 0;
1247 if(mSourceIds.empty())
1249 alGetError();
1250 alGenSources(1, &id);
1251 if(alGetError() == AL_NO_ERROR)
1252 return id;
1254 SourceImpl *lowest = nullptr;
1255 for(SourceBufferUpdateEntry &entry : mPlaySources)
1257 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1258 lowest = entry.mSource;
1260 for(SourceStreamUpdateEntry &entry : mStreamSources)
1262 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1263 lowest = entry.mSource;
1265 if(lowest && lowest->getPriority() < maxprio)
1267 lowest->stop();
1268 if(mMessage.get())
1269 mMessage->sourceForceStopped(lowest);
1272 if(mSourceIds.empty())
1273 throw std::runtime_error("No available sources");
1275 id = mSourceIds.back();
1276 mSourceIds.pop_back();
1277 return id;
1281 DECL_THUNK0(Source, Context, createSource,)
1282 Source ContextImpl::createSource()
1284 CheckContext(this);
1286 SourceImpl *source;
1287 if(!mFreeSources.empty())
1289 source = mFreeSources.back();
1290 mFreeSources.pop_back();
1292 else
1294 mAllSources.emplace_back(this);
1295 source = &mAllSources.back();
1297 return Source(source);
1301 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1303 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1304 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1305 { return lhs.mSource < rhs; }
1307 if(iter == mPendingSources.end() || iter->mSource != source)
1308 mPendingSources.insert(iter, {source, std::move(future)});
1311 void ContextImpl::removePendingSource(SourceImpl *source)
1313 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1314 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1315 { return lhs.mSource < rhs; }
1317 if(iter != mPendingSources.end() && iter->mSource == source)
1318 mPendingSources.erase(iter);
1321 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1323 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1324 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1325 { return lhs.mSource < rhs; }
1327 return (iter != mPendingSources.end() && iter->mSource == source);
1330 void ContextImpl::addFadingSource(SourceImpl *source)
1332 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1333 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1334 { return lhs < rhs; }
1336 if(iter == mFadingSources.end() || *iter != source)
1337 mFadingSources.insert(iter, source);
1340 void ContextImpl::removeFadingSource(SourceImpl *source)
1342 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1343 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1344 { return lhs < rhs; }
1346 if(iter != mFadingSources.end() && *iter == source)
1347 mFadingSources.erase(iter);
1350 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1352 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1353 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1354 { return lhs.mSource < rhs; }
1356 if(iter == mPlaySources.end() || iter->mSource != source)
1357 mPlaySources.insert(iter, {source,id});
1360 void ContextImpl::addPlayingSource(SourceImpl *source)
1362 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1363 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1364 { return lhs.mSource < rhs; }
1366 if(iter == mStreamSources.end() || iter->mSource != source)
1367 mStreamSources.insert(iter, {source});
1370 void ContextImpl::removePlayingSource(SourceImpl *source)
1372 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1373 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1374 { return lhs.mSource < rhs; }
1376 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1377 mPlaySources.erase(iter0);
1378 else
1380 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1381 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1382 { return lhs.mSource < rhs; }
1384 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1385 mStreamSources.erase(iter1);
1390 void ContextImpl::addStream(SourceImpl *source)
1392 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1393 if(mThread.get_id() == std::thread::id())
1394 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1395 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1396 if(iter == mStreamingSources.end() || *iter != source)
1397 mStreamingSources.insert(iter, source);
1400 void ContextImpl::removeStream(SourceImpl *source)
1402 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1403 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1404 if(iter != mStreamingSources.end() && *iter == source)
1405 mStreamingSources.erase(iter);
1408 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1410 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1411 if(iter != mStreamingSources.end() && *iter == source)
1412 mStreamingSources.erase(iter);
1416 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1417 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1419 if(!hasExtension(AL::EXT_EFX) || !alGenAuxiliaryEffectSlots)
1420 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1421 CheckContext(this);
1423 alGetError();
1424 ALuint id = 0;
1425 alGenAuxiliaryEffectSlots(1, &id);
1426 ALenum err = alGetError();
1427 if(err != AL_NO_ERROR)
1428 throw al_error(err, "Failed to create AuxiliaryEffectSlot");
1429 try {
1430 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1432 catch(...) {
1433 alDeleteAuxiliaryEffectSlots(1, &id);
1434 throw;
1439 DECL_THUNK0(Effect, Context, createEffect,)
1440 Effect ContextImpl::createEffect()
1442 if(!hasExtension(AL::EXT_EFX))
1443 throw std::runtime_error("Effects not supported");
1444 CheckContext(this);
1446 alGetError();
1447 ALuint id = 0;
1448 alGenEffects(1, &id);
1449 ALenum err = alGetError();
1450 if(err != AL_NO_ERROR)
1451 throw al_error(err, "Failed to create Effect");
1452 try {
1453 return Effect(new EffectImpl(this, id));
1455 catch(...) {
1456 alDeleteEffects(1, &id);
1457 throw;
1462 DECL_THUNK0(SourceGroup, Context, createSourceGroup,)
1463 SourceGroup ContextImpl::createSourceGroup()
1465 auto srcgroup = MakeUnique<SourceGroupImpl>(this);
1466 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), srcgroup);
1468 iter = mSourceGroups.insert(iter, std::move(srcgroup));
1469 return SourceGroup(iter->get());
1472 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1474 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group,
1475 [](const UniquePtr<SourceGroupImpl> &lhs, SourceGroupImpl *rhs) -> bool
1476 { return lhs.get() < rhs; }
1478 if(iter != mSourceGroups.end() && iter->get() == group)
1479 mSourceGroups.erase(iter);
1483 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1484 void ContextImpl::setDopplerFactor(ALfloat factor)
1486 if(!(factor >= 0.0f))
1487 throw std::out_of_range("Doppler factor out of range");
1488 CheckContext(this);
1489 alDopplerFactor(factor);
1493 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1494 void ContextImpl::setSpeedOfSound(ALfloat speed)
1496 if(!(speed > 0.0f))
1497 throw std::out_of_range("Speed of sound out of range");
1498 CheckContext(this);
1499 alSpeedOfSound(speed);
1503 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1504 void ContextImpl::setDistanceModel(DistanceModel model)
1506 CheckContext(this);
1507 alDistanceModel((ALenum)model);
1511 DECL_THUNK0(void, Context, update,)
1512 void ContextImpl::update()
1514 CheckContext(this);
1515 mPendingSources.erase(
1516 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1517 [](PendingSource &entry) -> bool
1518 { return !entry.mSource->checkPending(entry.mFuture); }
1519 ), mPendingSources.end()
1521 if(!mFadingSources.empty())
1523 auto cur_time = std::chrono::steady_clock::now();
1524 mFadingSources.erase(
1525 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1526 [cur_time](SourceImpl *source) -> bool
1527 { return !source->fadeUpdate(cur_time); }
1528 ), mFadingSources.end()
1531 mPlaySources.erase(
1532 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1533 [](const SourceBufferUpdateEntry &entry) -> bool
1534 { return !entry.mSource->playUpdate(entry.mId); }
1535 ), mPlaySources.end()
1537 mStreamSources.erase(
1538 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1539 [](const SourceStreamUpdateEntry &entry) -> bool
1540 { return !entry.mSource->playUpdate(); }
1541 ), mStreamSources.end()
1544 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1546 // For performance reasons, don't wait for the thread's mutex. This
1547 // should be called often enough to keep up with any and all streams
1548 // regardless.
1549 mWakeThread.notify_all();
1552 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1554 ALCint connected;
1555 alcGetIntegerv(mDevice->getALCdevice(), ALC_CONNECTED, 1, &connected);
1556 mIsConnected = connected;
1557 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1561 DECL_THUNK0(Device, Context, getDevice,)
1562 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1563 DECL_THUNK0(Listener, Context, getListener,)
1564 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1566 void Context::MakeCurrent(Context context)
1567 { ContextImpl::MakeCurrent(context.pImpl); }
1569 Context Context::GetCurrent()
1570 { return Context(ContextImpl::GetCurrent()); }
1572 void Context::MakeThreadCurrent(Context context)
1573 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1575 Context Context::GetThreadCurrent()
1576 { return Context(ContextImpl::GetThreadCurrent()); }
1579 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1580 void ListenerImpl::setGain(ALfloat gain)
1582 if(!(gain >= 0.0f))
1583 throw std::out_of_range("Gain out of range");
1584 CheckContext(mContext);
1585 alListenerf(AL_GAIN, gain);
1589 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1590 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1592 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1593 CheckContext(mContext);
1594 Batcher batcher = mContext->getBatcher();
1595 alListenerfv(AL_POSITION, position.getPtr());
1596 alListenerfv(AL_VELOCITY, velocity.getPtr());
1597 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1600 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1601 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1603 CheckContext(mContext);
1604 alListener3f(AL_POSITION, x, y, z);
1607 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1608 void ListenerImpl::setPosition(const ALfloat *pos)
1610 CheckContext(mContext);
1611 alListenerfv(AL_POSITION, pos);
1614 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1615 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1617 CheckContext(mContext);
1618 alListener3f(AL_VELOCITY, x, y, z);
1621 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1622 void ListenerImpl::setVelocity(const ALfloat *vel)
1624 CheckContext(mContext);
1625 alListenerfv(AL_VELOCITY, vel);
1628 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1629 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1631 CheckContext(mContext);
1632 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1633 alListenerfv(AL_ORIENTATION, ori);
1636 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1637 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1639 CheckContext(mContext);
1640 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1641 alListenerfv(AL_ORIENTATION, ori);
1644 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1645 void ListenerImpl::setOrientation(const ALfloat *ori)
1647 CheckContext(mContext);
1648 alListenerfv(AL_ORIENTATION, ori);
1651 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)
1652 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1654 if(!(m_u > 0.0f))
1655 throw std::out_of_range("Invalid meters per unit");
1656 CheckContext(mContext);
1657 if(mContext->hasExtension(AL::EXT_EFX))
1658 alListenerf(AL_METERS_PER_UNIT, m_u);