Fix an instance of return value elision
[alure.git] / src / context.cpp
blobc8cbcaff353bcdff889a685ef456a0c0f471c87a
2 #include "config.h"
4 #include "context.h"
6 #include <stdexcept>
7 #include <algorithm>
8 #include <functional>
9 #include <memory>
10 #include <iostream>
11 #include <sstream>
12 #include <fstream>
13 #include <cstring>
14 #include <map>
15 #include <new>
17 #include "alc.h"
19 #ifdef HAVE_VORBISFILE
20 #include "decoders/vorbisfile.hpp"
21 #endif
22 #ifdef HAVE_LIBFLAC
23 #include "decoders/flac.hpp"
24 #endif
25 #ifdef HAVE_OPUSFILE
26 #include "decoders/opusfile.hpp"
27 #endif
28 #ifdef HAVE_LIBSNDFILE
29 #include "decoders/sndfile.hpp"
30 #endif
31 #ifdef HAVE_MPG123
32 #include "decoders/mpg123.hpp"
33 #endif
34 #include "decoders/wave.hpp"
36 #include "devicemanager.h"
37 #include "device.h"
38 #include "buffer.h"
39 #include "source.h"
40 #include "auxeffectslot.h"
41 #include "effect.h"
42 #include "sourcegroup.h"
44 #ifdef _WIN32
45 #define WIN32_LEAN_AND_MEAN
46 #include <windows.h>
47 #endif
49 namespace std
52 // Implements a FNV-1a hash for StringView. NOTE: This is *NOT* guaranteed
53 // compatible with std::hash<String>! The standard does not give any specific
54 // hash implementation, nor a way for applications to access the same hash
55 // function as std::string (short of copying into a string and hashing that).
56 // So if you need Strings and StringViews to result in the same hash for the
57 // same set of characters, hash StringViews created from the Strings.
58 template<>
59 struct hash<alure::StringView> {
60 size_t operator()(const alure::StringView &str) const noexcept
62 using traits_type = alure::StringView::traits_type;
64 if /*constexpr*/ (sizeof(size_t) == 8)
66 static constexpr size_t hash_offset = 0xcbf29ce484222325;
67 static constexpr size_t hash_prime = 0x100000001b3;
69 size_t val = hash_offset;
70 for(auto ch : str)
71 val = (val^traits_type::to_int_type(ch)) * hash_prime;
72 return val;
74 else
76 static constexpr size_t hash_offset = 0x811c9dc5;
77 static constexpr size_t hash_prime = 0x1000193;
79 size_t val = hash_offset;
80 for(auto ch : str)
81 val = (val^traits_type::to_int_type(ch)) * hash_prime;
82 return val;
89 namespace
92 // Global mutex to protect global context changes
93 std::mutex mGlobalCtxMutex;
95 #ifdef _WIN32
96 // Windows' std::ifstream fails with non-ANSI paths since the standard only
97 // specifies names using const char* (or std::string). MSVC has a non-standard
98 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
99 // but not all Windows compilers support it. So we have to make our own istream
100 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
101 class StreamBuf final : public std::streambuf {
102 alure::Array<char_type,4096> mBuffer;
103 HANDLE mFile;
105 int_type underflow() override
107 if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
109 // Read in the next chunk of data, and set the pointers on success
110 DWORD got = 0;
111 if(!ReadFile(mFile, mBuffer.data(), mBuffer.size(), &got, NULL))
112 got = 0;
113 setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
115 if(gptr() == egptr())
116 return traits_type::eof();
117 return traits_type::to_int_type(*gptr());
120 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
122 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
123 return traits_type::eof();
125 LARGE_INTEGER fpos;
126 switch(whence)
128 case std::ios_base::beg:
129 fpos.QuadPart = offset;
130 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
131 return traits_type::eof();
132 break;
134 case std::ios_base::cur:
135 // If the offset remains in the current buffer range, just
136 // update the pointer.
137 if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
138 (offset < 0 && -offset <= off_type(gptr()-eback())))
140 // Get the current file offset to report the correct read
141 // offset.
142 fpos.QuadPart = 0;
143 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
144 return traits_type::eof();
145 setg(eback(), gptr()+offset, egptr());
146 return fpos.QuadPart - off_type(egptr()-gptr());
148 // Need to offset for the file offset being at egptr() while
149 // the requested offset is relative to gptr().
150 offset -= off_type(egptr()-gptr());
151 fpos.QuadPart = offset;
152 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
153 return traits_type::eof();
154 break;
156 case std::ios_base::end:
157 fpos.QuadPart = offset;
158 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
159 return traits_type::eof();
160 break;
162 default:
163 return traits_type::eof();
165 setg(0, 0, 0);
166 return fpos.QuadPart;
169 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
171 // Simplified version of seekoff
172 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
173 return traits_type::eof();
175 LARGE_INTEGER fpos;
176 fpos.QuadPart = pos;
177 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
178 return traits_type::eof();
180 setg(0, 0, 0);
181 return fpos.QuadPart;
184 public:
185 bool open(const char *filename)
187 alure::Vector<wchar_t> wname;
188 int wnamelen;
190 wnamelen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
191 if(wnamelen <= 0) return false;
193 wname.resize(wnamelen);
194 MultiByteToWideChar(CP_UTF8, 0, filename, -1, wname.data(), wnamelen);
196 mFile = CreateFileW(wname.data(), GENERIC_READ, FILE_SHARE_READ, NULL,
197 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
198 if(mFile == INVALID_HANDLE_VALUE) return false;
199 return true;
202 bool is_open() const { return mFile != INVALID_HANDLE_VALUE; }
204 StreamBuf() : mFile(INVALID_HANDLE_VALUE)
206 ~StreamBuf() override
208 if(mFile != INVALID_HANDLE_VALUE)
209 CloseHandle(mFile);
210 mFile = INVALID_HANDLE_VALUE;
214 // Inherit from std::istream to use our custom streambuf
215 class Stream final : public std::istream {
216 public:
217 Stream(const char *filename) : std::istream(new StreamBuf())
219 // Set the failbit if the file failed to open.
220 if(!(static_cast<StreamBuf*>(rdbuf())->open(filename)))
221 clear(failbit);
223 ~Stream() override
224 { delete rdbuf(); }
226 bool is_open() const { return static_cast<StreamBuf*>(rdbuf())->is_open(); }
228 #endif
230 using DecoderEntryPair = std::pair<alure::String,alure::UniquePtr<alure::DecoderFactory>>;
231 const DecoderEntryPair sDefaultDecoders[] = {
232 { "_alure_int_wave", alure::MakeUnique<alure::WaveDecoderFactory>() },
234 #ifdef HAVE_VORBISFILE
235 { "_alure_int_vorbis", alure::MakeUnique<alure::VorbisFileDecoderFactory>() },
236 #endif
237 #ifdef HAVE_LIBFLAC
238 { "_alure_int_flac", alure::MakeUnique<alure::FlacDecoderFactory>() },
239 #endif
240 #ifdef HAVE_OPUSFILE
241 { "_alure_int_opus", alure::MakeUnique<alure::OpusFileDecoderFactory>() },
242 #endif
243 #ifdef HAVE_LIBSNDFILE
244 { "_alure_int_sndfile", alure::MakeUnique<alure::SndFileDecoderFactory>() },
245 #endif
246 #ifdef HAVE_MPG123
247 { "_alure_int_mpg123", alure::MakeUnique<alure::Mpg123DecoderFactory>() },
248 #endif
250 alure::Vector<DecoderEntryPair> sDecoders;
253 template<typename T>
254 alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> &file,
255 T start, T end)
257 alure::DecoderOrExceptT ret;
258 while(start != end)
260 alure::DecoderFactory *factory = start->second.get();
261 auto decoder = factory->createDecoder(file);
262 if(decoder) return (ret = std::move(decoder));
264 if(!file || !(file->clear(),file->seekg(0)))
265 return (ret = std::runtime_error("Failed to rewind "+name+" for the next decoder factory"));
267 ++start;
270 return (ret = nullptr);
273 static alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> file)
275 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
276 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
277 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
278 decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
279 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
280 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
281 return (decoder = std::runtime_error("No decoder for "+name));
284 class DefaultFileIOFactory final : public alure::FileIOFactory {
285 alure::UniquePtr<std::istream> openFile(const alure::String &name) override
287 #ifdef _WIN32
288 auto file = alure::MakeUnique<Stream>(name.c_str());
289 #else
290 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
291 #endif
292 if(!file->is_open()) file = nullptr;
293 return std::move(file);
296 DefaultFileIOFactory sDefaultFileFactory;
298 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
302 namespace alure
305 Decoder::~Decoder() { }
306 DecoderFactory::~DecoderFactory() { }
308 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
310 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
311 [](const DecoderEntryPair &entry, StringView rhs) -> bool
312 { return entry.first < rhs; }
314 if(iter != sDecoders.end())
315 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
316 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
319 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name)
321 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
322 [](const DecoderEntryPair &entry, StringView rhs) -> bool
323 { return entry.first < rhs; }
325 if(iter != sDecoders.end())
327 UniquePtr<DecoderFactory> factory = std::move(iter->second);
328 sDecoders.erase(iter);
329 return factory;
331 return nullptr;
335 FileIOFactory::~FileIOFactory() { }
337 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
339 std::swap(sFileFactory, factory);
340 return factory;
343 FileIOFactory &FileIOFactory::get()
345 FileIOFactory *factory = sFileFactory.get();
346 if(factory) return *factory;
347 return sDefaultFileFactory;
351 // Default message handler methods are no-ops.
352 MessageHandler::~MessageHandler()
356 void MessageHandler::deviceDisconnected(Device)
360 void MessageHandler::sourceStopped(Source)
364 void MessageHandler::sourceForceStopped(Source)
368 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>)
372 String MessageHandler::resourceNotFound(StringView)
374 return String();
378 template<typename T>
379 static inline void LoadALFunc(T **func, const char *name)
380 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
382 static void LoadNothing(ContextImpl*) { }
384 static void LoadEFX(ContextImpl *ctx)
386 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
387 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
388 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
389 LoadALFunc(&ctx->alEffecti, "alEffecti");
390 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
391 LoadALFunc(&ctx->alEffectf, "alEffectf");
392 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
393 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
394 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
395 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
396 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
398 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
399 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
400 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
401 LoadALFunc(&ctx->alFilteri, "alFilteri");
402 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
403 LoadALFunc(&ctx->alFilterf, "alFilterf");
404 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
405 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
406 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
407 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
408 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
410 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
411 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
412 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
413 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
414 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
415 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
416 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
417 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
418 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
419 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
420 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
423 static void LoadSourceResampler(ContextImpl *ctx)
425 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
428 static void LoadSourceLatency(ContextImpl *ctx)
430 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
431 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
434 static const struct {
435 enum AL extension;
436 const char name[32];
437 void (&loader)(ContextImpl*);
438 } ALExtensionList[] = {
439 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
441 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
442 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
443 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
445 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
446 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
447 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
449 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
450 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
451 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
452 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
454 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
456 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
457 { AL::EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
461 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
462 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
464 std::atomic<uint64_t> ContextImpl::sContextSetCount{0};
466 void ContextImpl::MakeCurrent(ContextImpl *context)
468 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
470 if(alcMakeContextCurrent(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
471 throw std::runtime_error("Call to alcMakeContextCurrent failed");
472 if(context)
474 context->addRef();
475 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
477 std::swap(sCurrentCtx, context);
478 if(context) context->decRef();
480 if(sThreadCurrentCtx)
481 sThreadCurrentCtx->decRef();
482 sThreadCurrentCtx = nullptr;
483 sContextSetCount.fetch_add(1, std::memory_order_release);
485 if((context = sCurrentCtx) != nullptr)
487 ctxlock.unlock();
488 context->mWakeThread.notify_all();
492 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
494 if(!DeviceManagerImpl::SetThreadContext)
495 throw std::runtime_error("Thread-local contexts unsupported");
496 if(DeviceManagerImpl::SetThreadContext(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
497 throw std::runtime_error("Call to alcSetThreadContext failed");
498 if(context)
500 context->addRef();
501 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
503 if(sThreadCurrentCtx)
504 sThreadCurrentCtx->decRef();
505 sThreadCurrentCtx = context;
506 sContextSetCount.fetch_add(1, std::memory_order_release);
509 void ContextImpl::setupExts()
511 ALCdevice *device = mDevice->getALCdevice();
512 mHasExt.clear();
513 for(const auto &entry : ALExtensionList)
515 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
516 alIsExtensionPresent(entry.name))
518 mHasExt.set(static_cast<size_t>(entry.extension));
519 entry.loader(this);
525 void ContextImpl::backgroundProc()
527 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(ALC::EXT_thread_local_context))
528 DeviceManagerImpl::SetThreadContext(getALCcontext());
530 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
531 std::chrono::milliseconds waketime(0);
532 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
533 while(!mQuitThread.load(std::memory_order_acquire))
536 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
537 mStreamingSources.erase(
538 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
539 [](SourceImpl *source) -> bool
540 { return !source->updateAsync(); }
541 ), mStreamingSources.end()
545 // Only do one pending buffer at a time. In case there's several large
546 // buffers to load, we still need to process streaming sources so they
547 // don't underrun.
548 PendingBuffer *lastpb = mPendingCurrent.load(std::memory_order_acquire);
549 if(PendingBuffer *pb = lastpb->mNext.load(std::memory_order_relaxed))
551 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
552 pb->mPromise.set_value(Buffer(pb->mBuffer));
553 Promise<Buffer>().swap(pb->mPromise);
554 mPendingCurrent.store(pb, std::memory_order_release);
555 continue;
558 std::unique_lock<std::mutex> wakelock(mWakeMutex);
559 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
561 ctxlock.unlock();
563 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
564 if(interval.count() == 0)
565 mWakeThread.wait(wakelock);
566 else
568 auto now = std::chrono::steady_clock::now() - basetime;
569 if(now > waketime)
571 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
572 waketime += interval * mult;
574 mWakeThread.wait_until(wakelock, waketime + basetime);
576 wakelock.unlock();
578 ctxlock.lock();
579 while(!mQuitThread.load(std::memory_order_acquire) &&
580 alcGetCurrentContext() != getALCcontext())
581 mWakeThread.wait(ctxlock);
584 ctxlock.unlock();
586 if(DeviceManagerImpl::SetThreadContext)
587 DeviceManagerImpl::SetThreadContext(nullptr);
591 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
592 : mContextSetCounter(std::numeric_limits<uint64_t>::max()),
593 mListener(this), mContext(context), mDevice(device),
594 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
595 mRefs(0), mIsConnected(true), mIsBatching(false),
596 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
597 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
598 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
599 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
600 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
601 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
602 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
603 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
604 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
605 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
607 mHasExt.clear();
608 mPendingHead = new PendingBuffer;
609 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
610 mPendingTail = mPendingHead;
613 ContextImpl::~ContextImpl()
615 PendingBuffer *pb = mPendingTail;
616 while(pb)
618 PendingBuffer *next = pb->mNext.load(std::memory_order_relaxed);
619 delete pb;
620 pb = next;
622 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
623 mPendingHead = nullptr;
624 mPendingTail = nullptr;
628 void ContextImpl::destroy()
630 if(mRefs != 0)
631 throw std::runtime_error("Context is in use");
632 if(!mBuffers.empty())
633 throw std::runtime_error("Trying to destroy a context with buffers");
635 if(mThread.joinable())
637 std::unique_lock<std::mutex> lock(mWakeMutex);
638 mQuitThread.store(true, std::memory_order_release);
639 lock.unlock();
640 mWakeThread.notify_all();
641 mThread.join();
644 alcDestroyContext(mContext);
645 mContext = nullptr;
647 mDevice->removeContext(this);
651 void ContextImpl::startBatch()
653 alcSuspendContext(mContext);
654 mIsBatching = true;
657 void ContextImpl::endBatch()
659 alcProcessContext(mContext);
660 mIsBatching = false;
664 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler>&& handler)
666 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
667 mMessage.swap(handler);
668 return handler;
672 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
674 if(interval.count() < 0 || interval > std::chrono::seconds(1))
675 throw std::runtime_error("Async wake interval out of range");
676 mWakeInterval.store(interval);
677 mWakeMutex.lock(); mWakeMutex.unlock();
678 mWakeThread.notify_all();
682 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
684 DecoderOrExceptT ret;
686 String oldname = String(name);
687 auto file = FileIOFactory::get().openFile(oldname);
688 if(file) return (ret = GetDecoder(name, std::move(file)));
690 // Resource not found. Try to find a substitute.
691 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+oldname));
692 do {
693 String newname(mMessage->resourceNotFound(oldname));
694 if(newname.empty())
695 return (ret = std::runtime_error("Failed to open "+oldname));
696 file = FileIOFactory::get().openFile(newname);
697 oldname = std::move(newname);
698 } while(!file);
700 return (ret = GetDecoder(oldname, std::move(file)));
703 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
705 CheckContext(this);
706 DecoderOrExceptT dec = findDecoder(name);
707 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
708 return *decoder;
709 throw std::get<std::runtime_error>(dec);
713 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
715 CheckContext(this);
716 return GetFormat(channels, type) != AL_NONE;
720 ArrayView<String> ContextImpl::getAvailableResamplers()
722 CheckContext(this);
723 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
725 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
726 mResamplers.reserve(num_resamplers);
727 for(int i = 0;i < num_resamplers;i++)
728 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
729 if(mResamplers.empty())
730 mResamplers.emplace_back();
732 return mResamplers;
735 ALsizei ContextImpl::getDefaultResamplerIndex() const
737 CheckContext(this);
738 if(!hasExtension(AL::SOFT_source_resampler))
739 return 0;
740 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
744 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
746 BufferOrExceptT retval;
747 ALuint srate = decoder->getFrequency();
748 ChannelConfig chans = decoder->getChannelConfig();
749 SampleType type = decoder->getSampleType();
750 ALuint frames = decoder->getLength();
752 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
753 frames = decoder->read(data.data(), frames);
754 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
755 data.resize(FramesToBytes(frames, chans, type));
757 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
758 if(loop_pts.first >= loop_pts.second)
759 loop_pts = std::make_pair(0, frames);
760 else
762 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
763 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
766 // Get the format before calling the bufferLoading message handler, to
767 // ensure it's something OpenAL can handle.
768 ALenum format = GetFormat(chans, type);
769 if(format == AL_NONE)
771 std::stringstream sstr;
772 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
773 return (retval = std::runtime_error(sstr.str()));
776 if(mMessage.get())
777 mMessage->bufferLoading(name, chans, type, srate, data);
779 alGetError();
780 ALuint bid = 0;
781 alGenBuffers(1, &bid);
782 alBufferData(bid, format, data.data(), data.size(), srate);
783 if(hasExtension(AL::SOFT_loop_points))
785 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
786 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
788 if(alGetError() != AL_NO_ERROR)
790 alDeleteBuffers(1, &bid);
791 return (retval = std::runtime_error("Failed to buffer data"));
794 return (retval = mBuffers.insert(iter,
795 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
796 )->get());
799 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
801 BufferOrExceptT retval;
802 ALuint srate = decoder->getFrequency();
803 ChannelConfig chans = decoder->getChannelConfig();
804 SampleType type = decoder->getSampleType();
805 ALuint frames = decoder->getLength();
806 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
808 ALenum format = GetFormat(chans, type);
809 if(format == AL_NONE)
811 std::stringstream sstr;
812 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
813 return (retval = std::runtime_error(sstr.str()));
816 alGetError();
817 ALuint bid = 0;
818 alGenBuffers(1, &bid);
819 if(alGetError() != AL_NO_ERROR)
820 return (retval = std::runtime_error("Failed to create buffer"));
822 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
824 if(mThread.get_id() == std::thread::id())
825 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
827 PendingBuffer *pb = nullptr;
828 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
829 pb = new PendingBuffer{buffer.get(), decoder, format, frames, std::move(promise)};
830 else
832 pb = mPendingTail;
833 pb->mBuffer = buffer.get();
834 pb->mDecoder = decoder;
835 pb->mFormat = format;
836 pb->mFrames = frames;
837 pb->mPromise = std::move(promise);
838 mPendingTail = pb->mNext.exchange(nullptr, std::memory_order_relaxed);
841 mPendingHead->mNext.store(pb, std::memory_order_release);
842 mPendingHead = pb;
844 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
847 Buffer ContextImpl::getBuffer(StringView name)
849 CheckContext(this);
851 auto hasher = std::hash<StringView>();
852 if(UNLIKELY(!mFutureBuffers.empty()))
854 Buffer buffer;
856 // If the buffer is already pending for the future, wait for it
857 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
858 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
859 { return hasher(lhs.mBuffer->getName()) < rhs; }
861 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
863 buffer = iter->mFuture.get();
864 mFutureBuffers.erase(iter);
867 // Clear out any completed futures.
868 mFutureBuffers.erase(
869 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
870 [](const PendingFuture &entry) -> bool
871 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
872 ), mFutureBuffers.end()
875 // If we got the buffer, return it. Otherwise, go load it normally.
876 if(buffer) return buffer;
879 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
880 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
881 { return hasher(lhs->getName()) < rhs; }
883 if(iter != mBuffers.end() && (*iter)->getName() == name)
884 return Buffer(iter->get());
886 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
887 Buffer *buffer = std::get_if<Buffer>(&ret);
888 if(UNLIKELY(!buffer))
889 throw std::get<std::runtime_error>(ret);
890 return *buffer;
893 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
895 SharedFuture<Buffer> future;
896 CheckContext(this);
898 auto hasher = std::hash<StringView>();
899 if(UNLIKELY(!mFutureBuffers.empty()))
901 // Check if the future that's being created already exists
902 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
903 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
904 { return hasher(lhs.mBuffer->getName()) < rhs; }
906 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
908 future = iter->mFuture;
909 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
910 mFutureBuffers.erase(iter);
911 return future;
914 // Clear out any fulfilled futures.
915 mFutureBuffers.erase(
916 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
917 [](const PendingFuture &entry) -> bool
918 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
919 ), mFutureBuffers.end()
923 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
924 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
925 { return hasher(lhs->getName()) < rhs; }
927 if(iter != mBuffers.end() && (*iter)->getName() == name)
929 // User asked to create a future buffer that's already loaded. Just
930 // construct a promise, fulfill the promise immediately, then return a
931 // shared future that's already set.
932 Promise<Buffer> promise;
933 promise.set_value(Buffer(iter->get()));
934 future = promise.get_future().share();
935 return future;
938 Promise<Buffer> promise;
939 future = promise.get_future().share();
941 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
942 Buffer *buffer = std::get_if<Buffer>(&ret);
943 if(UNLIKELY(!buffer))
944 throw std::get<std::runtime_error>(ret);
945 mWakeMutex.lock(); mWakeMutex.unlock();
946 mWakeThread.notify_all();
948 mFutureBuffers.insert(
949 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
950 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
951 { return hasher(lhs.mBuffer->getName()) < rhs; }
952 ), { buffer->getHandle(), future }
955 return future;
958 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
960 CheckContext(this);
962 if(UNLIKELY(!mFutureBuffers.empty()))
964 // Clear out any fulfilled futures.
965 mFutureBuffers.erase(
966 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
967 [](const PendingFuture &entry) -> bool
968 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
969 ), mFutureBuffers.end()
973 auto hasher = std::hash<StringView>();
974 for(const StringView name : names)
976 // Check if the buffer that's being created already exists
977 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
978 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
979 { return hasher(lhs->getName()) < rhs; }
981 if(iter != mBuffers.end() && (*iter)->getName() == name)
982 continue;
984 DecoderOrExceptT dec = findDecoder(name);
985 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
986 if(!decoder) continue;
988 Promise<Buffer> promise;
989 SharedFuture<Buffer> future = promise.get_future().share();
991 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
992 std::move(promise));
993 Buffer *buffer = std::get_if<Buffer>(&buf);
994 if(UNLIKELY(!buffer)) continue;
996 mFutureBuffers.insert(
997 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
998 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
999 { return hasher(lhs.mBuffer->getName()) < rhs; }
1000 ), { buffer->getHandle(), future }
1003 mWakeMutex.lock(); mWakeMutex.unlock();
1004 mWakeThread.notify_all();
1007 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder>&& decoder)
1009 CheckContext(this);
1011 auto hasher = std::hash<StringView>();
1012 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1013 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1014 { return hasher(lhs->getName()) < rhs; }
1016 if(iter != mBuffers.end() && (*iter)->getName() == name)
1017 throw std::runtime_error("Buffer \""+name+"\" already exists");
1019 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1020 Buffer *buffer = std::get_if<Buffer>(&ret);
1021 if(UNLIKELY(!buffer))
1022 throw std::get<std::runtime_error>(ret);
1023 return *buffer;
1026 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder>&& decoder)
1028 SharedFuture<Buffer> future;
1029 CheckContext(this);
1031 if(UNLIKELY(!mFutureBuffers.empty()))
1033 // Clear out any fulfilled futures.
1034 mFutureBuffers.erase(
1035 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1036 [](const PendingFuture &entry) -> bool
1037 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1038 ), mFutureBuffers.end()
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 \""+name+"\" already exists");
1050 Promise<Buffer> promise;
1051 future = promise.get_future().share();
1053 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1054 Buffer *buffer = std::get_if<Buffer>(&ret);
1055 if(UNLIKELY(!buffer))
1056 throw std::get<std::runtime_error>(ret);
1057 mWakeMutex.lock(); mWakeMutex.unlock();
1058 mWakeThread.notify_all();
1060 mFutureBuffers.insert(
1061 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1062 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1063 { return hasher(lhs.mBuffer->getName()) < rhs; }
1064 ), { buffer->getHandle(), future }
1067 return future;
1071 Buffer ContextImpl::findBuffer(StringView name)
1073 Buffer buffer;
1074 CheckContext(this);
1076 auto hasher = std::hash<StringView>();
1077 if(UNLIKELY(!mFutureBuffers.empty()))
1079 // If the buffer is already pending for the future, wait for it
1080 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1081 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1082 { return hasher(lhs.mBuffer->getName()) < rhs; }
1084 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1086 buffer = iter->mFuture.get();
1087 mFutureBuffers.erase(iter);
1090 // Clear out any completed futures.
1091 mFutureBuffers.erase(
1092 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1093 [](const PendingFuture &entry) -> bool
1094 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1095 ), mFutureBuffers.end()
1099 if(LIKELY(!buffer))
1101 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1102 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1103 { return hasher(lhs->getName()) < rhs; }
1105 if(iter != mBuffers.end() && (*iter)->getName() == name)
1106 buffer = Buffer(iter->get());
1108 return buffer;
1111 SharedFuture<Buffer> ContextImpl::findBufferAsync(StringView name)
1113 SharedFuture<Buffer> future;
1114 CheckContext(this);
1116 auto hasher = std::hash<StringView>();
1117 if(UNLIKELY(!mFutureBuffers.empty()))
1119 // Check if the future that's being created already exists
1120 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1121 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1122 { return hasher(lhs.mBuffer->getName()) < rhs; }
1124 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1126 future = iter->mFuture;
1127 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
1128 mFutureBuffers.erase(iter);
1129 return future;
1132 // Clear out any fulfilled futures.
1133 mFutureBuffers.erase(
1134 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1135 [](const PendingFuture &entry) -> bool
1136 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1137 ), mFutureBuffers.end()
1141 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1142 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1143 { return hasher(lhs->getName()) < rhs; }
1145 if(iter != mBuffers.end() && (*iter)->getName() == name)
1147 // User asked to create a future buffer that's already loaded. Just
1148 // construct a promise, fulfill the promise immediately, then return a
1149 // shared future that's already set.
1150 Promise<Buffer> promise;
1151 promise.set_value(Buffer(iter->get()));
1152 future = promise.get_future().share();
1154 return future;
1158 void ContextImpl::removeBuffer(StringView name)
1160 CheckContext(this);
1162 auto hasher = std::hash<StringView>();
1163 if(UNLIKELY(!mFutureBuffers.empty()))
1165 // If the buffer is already pending for the future, wait for it to
1166 // finish before continuing.
1167 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1168 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1169 { return hasher(lhs.mBuffer->getName()) < rhs; }
1171 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1173 iter->mFuture.wait();
1174 mFutureBuffers.erase(iter);
1177 // Clear out any completed futures.
1178 mFutureBuffers.erase(
1179 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1180 [](const PendingFuture &entry) -> bool
1181 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1182 ), mFutureBuffers.end()
1186 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1187 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1188 { return hasher(lhs->getName()) < rhs; }
1190 if(iter != mBuffers.end() && (*iter)->getName() == name)
1192 // Remove pending sources whose future was waiting for this buffer.
1193 mPendingSources.erase(
1194 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1195 [iter](PendingSource &entry) -> bool
1197 return (entry.mFuture.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready &&
1198 entry.mFuture.get().getHandle() == iter->get());
1200 ), mPendingSources.end()
1202 (*iter)->cleanup();
1203 mBuffers.erase(iter);
1208 ALuint ContextImpl::getSourceId(ALuint maxprio)
1210 ALuint id = 0;
1211 if(mSourceIds.empty())
1213 alGetError();
1214 alGenSources(1, &id);
1215 if(alGetError() == AL_NO_ERROR)
1216 return id;
1218 SourceImpl *lowest = nullptr;
1219 for(SourceBufferUpdateEntry &entry : mPlaySources)
1221 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1222 lowest = entry.mSource;
1224 for(SourceStreamUpdateEntry &entry : mStreamSources)
1226 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1227 lowest = entry.mSource;
1229 if(lowest && lowest->getPriority() < maxprio)
1231 lowest->stop();
1232 if(mMessage.get())
1233 mMessage->sourceForceStopped(lowest);
1236 if(mSourceIds.empty())
1237 throw std::runtime_error("No available sources");
1239 id = mSourceIds.top();
1240 mSourceIds.pop();
1241 return id;
1245 Source ContextImpl::createSource()
1247 CheckContext(this);
1249 SourceImpl *source;
1250 if(!mFreeSources.empty())
1252 source = mFreeSources.back();
1253 mFreeSources.pop_back();
1255 else
1257 mAllSources.emplace_back(this);
1258 source = &mAllSources.back();
1260 return Source(source);
1264 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1266 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1267 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1268 { return lhs.mSource < rhs; }
1270 if(iter == mPendingSources.end() || iter->mSource != source)
1271 mPendingSources.insert(iter, {source, std::move(future)});
1274 void ContextImpl::removePendingSource(SourceImpl *source)
1276 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1277 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1278 { return lhs.mSource < rhs; }
1280 if(iter != mPendingSources.end() && iter->mSource == source)
1281 mPendingSources.erase(iter);
1284 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1286 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1287 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1288 { return lhs.mSource < rhs; }
1290 return (iter != mPendingSources.end() && iter->mSource == source);
1293 void ContextImpl::addFadingSource(SourceImpl *source)
1295 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1296 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1297 { return lhs < rhs; }
1299 if(iter == mFadingSources.end() || *iter != source)
1300 mFadingSources.insert(iter, source);
1303 void ContextImpl::removeFadingSource(SourceImpl *source)
1305 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1306 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1307 { return lhs < rhs; }
1309 if(iter != mFadingSources.end() && *iter == source)
1310 mFadingSources.erase(iter);
1313 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1315 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1316 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1317 { return lhs.mSource < rhs; }
1319 if(iter == mPlaySources.end() || iter->mSource != source)
1320 mPlaySources.insert(iter, {source,id});
1323 void ContextImpl::addPlayingSource(SourceImpl *source)
1325 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1326 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1327 { return lhs.mSource < rhs; }
1329 if(iter == mStreamSources.end() || iter->mSource != source)
1330 mStreamSources.insert(iter, {source});
1333 void ContextImpl::removePlayingSource(SourceImpl *source)
1335 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1336 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1337 { return lhs.mSource < rhs; }
1339 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1340 mPlaySources.erase(iter0);
1341 else
1343 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1344 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1345 { return lhs.mSource < rhs; }
1347 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1348 mStreamSources.erase(iter1);
1353 void ContextImpl::addStream(SourceImpl *source)
1355 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1356 if(mThread.get_id() == std::thread::id())
1357 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1358 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1359 if(iter == mStreamingSources.end() || *iter != source)
1360 mStreamingSources.insert(iter, source);
1363 void ContextImpl::removeStream(SourceImpl *source)
1365 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1366 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1367 if(iter != mStreamingSources.end() && *iter == source)
1368 mStreamingSources.erase(iter);
1371 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1373 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1374 if(iter != mStreamingSources.end() && *iter == source)
1375 mStreamingSources.erase(iter);
1379 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1381 if(!hasExtension(AL::EXT_EFX) || !alGenAuxiliaryEffectSlots)
1382 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1383 CheckContext(this);
1385 alGetError();
1386 ALuint id = 0;
1387 alGenAuxiliaryEffectSlots(1, &id);
1388 if(alGetError() != AL_NO_ERROR)
1389 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1390 try {
1391 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1393 catch(...) {
1394 alDeleteAuxiliaryEffectSlots(1, &id);
1395 throw;
1400 Effect ContextImpl::createEffect()
1402 if(!hasExtension(AL::EXT_EFX))
1403 throw std::runtime_error("Effects not supported");
1404 CheckContext(this);
1406 alGetError();
1407 ALuint id = 0;
1408 alGenEffects(1, &id);
1409 if(alGetError() != AL_NO_ERROR)
1410 throw std::runtime_error("Failed to create Effect");
1411 try {
1412 return Effect(new EffectImpl(this, id));
1414 catch(...) {
1415 alDeleteEffects(1, &id);
1416 throw;
1421 SourceGroup ContextImpl::createSourceGroup(StringView name)
1423 auto hasher = std::hash<StringView>();
1424 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1425 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1426 { return hasher(lhs->getName()) < rhs; }
1428 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1429 throw std::runtime_error("Duplicate source group name");
1430 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, name));
1431 return SourceGroup(iter->get());
1434 SourceGroup ContextImpl::getSourceGroup(StringView name)
1436 auto hasher = std::hash<StringView>();
1437 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1438 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1439 { return hasher(lhs->getName()) < rhs; }
1441 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1442 throw std::runtime_error("Source group not found");
1443 return SourceGroup(iter->get());
1446 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1448 auto hasher = std::hash<StringView>();
1449 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(group->getName()),
1450 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1451 { return hasher(lhs->getName()) < rhs; }
1453 if(iter != mSourceGroups.end() && iter->get() == group)
1454 mSourceGroups.erase(iter);
1458 void ContextImpl::setDopplerFactor(ALfloat factor)
1460 if(!(factor >= 0.0f))
1461 throw std::runtime_error("Doppler factor out of range");
1462 CheckContext(this);
1463 alDopplerFactor(factor);
1467 void ContextImpl::setSpeedOfSound(ALfloat speed)
1469 if(!(speed > 0.0f))
1470 throw std::runtime_error("Speed of sound out of range");
1471 CheckContext(this);
1472 alSpeedOfSound(speed);
1476 void ContextImpl::setDistanceModel(DistanceModel model)
1478 CheckContext(this);
1479 alDistanceModel((ALenum)model);
1483 void ContextImpl::update()
1485 CheckContext(this);
1486 mPendingSources.erase(
1487 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1488 [](PendingSource &entry) -> bool
1489 { return !entry.mSource->checkPending(entry.mFuture); }
1490 ), mPendingSources.end()
1492 if(!mFadingSources.empty())
1494 auto cur_time = std::chrono::steady_clock::now();
1495 mFadingSources.erase(
1496 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1497 [cur_time](SourceImpl *source) -> bool
1498 { return !source->fadeUpdate(cur_time); }
1499 ), mFadingSources.end()
1502 mPlaySources.erase(
1503 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1504 [](const SourceBufferUpdateEntry &entry) -> bool
1505 { return !entry.mSource->playUpdate(entry.mId); }
1506 ), mPlaySources.end()
1508 mStreamSources.erase(
1509 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1510 [](const SourceStreamUpdateEntry &entry) -> bool
1511 { return !entry.mSource->playUpdate(); }
1512 ), mStreamSources.end()
1515 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1517 // For performance reasons, don't wait for the thread's mutex. This
1518 // should be called often enough to keep up with any and all streams
1519 // regardless.
1520 mWakeThread.notify_all();
1523 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1525 ALCint connected;
1526 alcGetIntegerv(mDevice->getALCdevice(), ALC_CONNECTED, 1, &connected);
1527 mIsConnected = connected;
1528 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1532 void Context::destroy()
1534 pImpl->destroy();
1535 pImpl = nullptr;
1537 DECL_THUNK0(Device, Context, getDevice,)
1538 DECL_THUNK0(void, Context, startBatch,)
1539 DECL_THUNK0(void, Context, endBatch,)
1540 DECL_THUNK0(Listener, Context, getListener,)
1541 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1542 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1543 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1544 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1545 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
1546 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1547 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1548 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1549 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1550 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1551 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1552 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1553 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1554 DECL_THUNK1(Buffer, Context, findBuffer,, StringView)
1555 DECL_THUNK1(SharedFuture<Buffer>, Context, findBufferAsync,, StringView)
1556 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1557 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1558 DECL_THUNK0(Source, Context, createSource,)
1559 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1560 DECL_THUNK0(Effect, Context, createEffect,)
1561 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, StringView)
1562 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, StringView)
1563 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1564 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1565 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1566 DECL_THUNK0(void, Context, update,)
1569 void Context::MakeCurrent(Context context)
1570 { ContextImpl::MakeCurrent(context.pImpl); }
1572 Context Context::GetCurrent()
1573 { return Context(ContextImpl::GetCurrent()); }
1575 void Context::MakeThreadCurrent(Context context)
1576 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1578 Context Context::GetThreadCurrent()
1579 { return Context(ContextImpl::GetThreadCurrent()); }
1582 void ListenerImpl::setGain(ALfloat gain)
1584 if(!(gain >= 0.0f))
1585 throw std::runtime_error("Gain out of range");
1586 CheckContext(mContext);
1587 alListenerf(AL_GAIN, gain);
1591 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1593 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1594 CheckContext(mContext);
1595 Batcher batcher = mContext->getBatcher();
1596 alListenerfv(AL_POSITION, position.getPtr());
1597 alListenerfv(AL_VELOCITY, velocity.getPtr());
1598 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1601 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1603 CheckContext(mContext);
1604 alListener3f(AL_POSITION, x, y, z);
1607 void ListenerImpl::setPosition(const ALfloat *pos)
1609 CheckContext(mContext);
1610 alListenerfv(AL_POSITION, pos);
1613 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1615 CheckContext(mContext);
1616 alListener3f(AL_VELOCITY, x, y, z);
1619 void ListenerImpl::setVelocity(const ALfloat *vel)
1621 CheckContext(mContext);
1622 alListenerfv(AL_VELOCITY, vel);
1625 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1627 CheckContext(mContext);
1628 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1629 alListenerfv(AL_ORIENTATION, ori);
1632 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1634 CheckContext(mContext);
1635 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1636 alListenerfv(AL_ORIENTATION, ori);
1639 void ListenerImpl::setOrientation(const ALfloat *ori)
1641 CheckContext(mContext);
1642 alListenerfv(AL_ORIENTATION, ori);
1645 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1647 if(!(m_u > 0.0f))
1648 throw std::runtime_error("Invalid meters per unit");
1649 CheckContext(mContext);
1650 if(mContext->hasExtension(AL::EXT_EFX))
1651 alListenerf(AL_METERS_PER_UNIT, m_u);
1655 using Vector3Pair = std::pair<Vector3,Vector3>;
1657 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1658 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1659 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1660 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1661 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1662 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1663 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1664 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1665 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1666 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)