Update the OpenAL Soft download
[alure.git] / src / context.cpp
blobddaa7edeeb56829d6ce8789e12ea48747b00ee25
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 <fstream>
12 #include <cstring>
13 #include <map>
14 #include <new>
16 #include "alc.h"
18 #ifdef HAVE_WAVE
19 #include "decoders/wave.hpp"
20 #endif
21 #ifdef HAVE_VORBISFILE
22 #include "decoders/vorbisfile.hpp"
23 #endif
24 #ifdef HAVE_FLAC
25 #include "decoders/flac.hpp"
26 #endif
27 #ifdef HAVE_OPUSFILE
28 #include "decoders/opusfile.hpp"
29 #endif
30 #ifdef HAVE_LIBSNDFILE
31 #include "decoders/sndfile.hpp"
32 #endif
33 #ifdef HAVE_MINIMP3
34 #include "decoders/mp3.hpp"
35 #endif
37 #include "devicemanager.h"
38 #include "device.h"
39 #include "buffer.h"
40 #include "source.h"
41 #include "auxeffectslot.h"
42 #include "effect.h"
43 #include "sourcegroup.h"
45 #ifdef _WIN32
46 #define WIN32_LEAN_AND_MEAN
47 #include <windows.h>
48 #endif
50 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 {
91 // Global mutex to protect global context changes
92 std::mutex gGlobalCtxMutex;
94 #ifdef _WIN32
95 // Windows' std::ifstream fails with non-ANSI paths since the standard only
96 // specifies names using const char* (or std::string). MSVC has a non-standard
97 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
98 // but not all Windows compilers support it. So we have to make our own istream
99 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
100 class StreamBuf final : public std::streambuf {
101 alure::Array<char_type,4096> mBuffer;
102 HANDLE mFile{INVALID_HANDLE_VALUE};
104 int_type underflow() override
106 if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
108 // Read in the next chunk of data, and set the pointers on success
109 DWORD got = 0;
110 if(ReadFile(mFile, mBuffer.data(), (DWORD)mBuffer.size(), &got, NULL))
111 setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
113 if(gptr() == egptr())
114 return traits_type::eof();
115 return traits_type::to_int_type(*gptr());
118 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
120 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
121 return traits_type::eof();
123 LARGE_INTEGER fpos;
124 switch(whence)
126 case std::ios_base::beg:
127 fpos.QuadPart = offset;
128 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
129 return traits_type::eof();
130 break;
132 case std::ios_base::cur:
133 // If the offset remains in the current buffer range, just
134 // update the pointer.
135 if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
136 (offset < 0 && -offset <= off_type(gptr()-eback())))
138 // Get the current file offset to report the correct read
139 // offset.
140 fpos.QuadPart = 0;
141 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
142 return traits_type::eof();
143 setg(eback(), gptr()+offset, egptr());
144 return fpos.QuadPart - off_type(egptr()-gptr());
146 // Need to offset for the file offset being at egptr() while
147 // the requested offset is relative to gptr().
148 offset -= off_type(egptr()-gptr());
149 fpos.QuadPart = offset;
150 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
151 return traits_type::eof();
152 break;
154 case std::ios_base::end:
155 fpos.QuadPart = offset;
156 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
157 return traits_type::eof();
158 break;
160 default:
161 return traits_type::eof();
163 setg(nullptr, nullptr, nullptr);
164 return fpos.QuadPart;
167 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
169 // Simplified version of seekoff
170 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
171 return traits_type::eof();
173 LARGE_INTEGER fpos;
174 fpos.QuadPart = pos;
175 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
176 return traits_type::eof();
178 setg(nullptr, nullptr, nullptr);
179 return fpos.QuadPart;
182 public:
183 bool open(const char *filename)
185 alure::Vector<wchar_t> wname;
186 int wnamelen;
188 wnamelen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
189 if(wnamelen <= 0) return false;
191 wname.resize(wnamelen);
192 MultiByteToWideChar(CP_UTF8, 0, filename, -1, wname.data(), wnamelen);
194 mFile = CreateFileW(wname.data(), GENERIC_READ, FILE_SHARE_READ, NULL,
195 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
196 if(mFile == INVALID_HANDLE_VALUE) return false;
197 return true;
200 bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; }
202 StreamBuf() = default;
203 ~StreamBuf() override
205 if(mFile != INVALID_HANDLE_VALUE)
206 CloseHandle(mFile);
207 mFile = INVALID_HANDLE_VALUE;
211 // Inherit from std::istream to use our custom streambuf
212 class Stream final : public std::istream {
213 StreamBuf mStreamBuf;
215 public:
216 Stream(const char *filename) : std::istream(nullptr)
218 init(&mStreamBuf);
220 // Set the failbit if the file failed to open.
221 if(!mStreamBuf.open(filename)) clear(failbit);
224 bool is_open() const noexcept { return mStreamBuf.is_open(); }
226 #endif
228 using DecoderEntryPair = std::pair<alure::String,alure::UniquePtr<alure::DecoderFactory>>;
229 const DecoderEntryPair sDefaultDecoders[] = {
230 #ifdef HAVE_WAVE
231 { "_alure_int_wave", alure::MakeUnique<alure::WaveDecoderFactory>() },
232 #endif
233 #ifdef HAVE_VORBISFILE
234 { "_alure_int_vorbis", alure::MakeUnique<alure::VorbisFileDecoderFactory>() },
235 #endif
236 #ifdef HAVE_FLAC
237 { "_alure_int_flac", alure::MakeUnique<alure::FlacDecoderFactory>() },
238 #endif
239 #ifdef HAVE_OPUSFILE
240 { "_alure_int_opus", alure::MakeUnique<alure::OpusFileDecoderFactory>() },
241 #endif
242 #ifdef HAVE_LIBSNDFILE
243 { "_alure_int_sndfile", alure::MakeUnique<alure::SndFileDecoderFactory>() },
244 #endif
245 #ifdef HAVE_MINIMP3
246 { "_alure_int_minimp3", alure::MakeUnique<alure::Mp3DecoderFactory>() },
247 #endif
249 alure::Vector<DecoderEntryPair> sDecoders;
252 alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> &file,
253 alure::ArrayView<DecoderEntryPair> decoders)
255 while(!decoders.empty())
257 alure::DecoderFactory *factory = decoders.front().second.get();
258 auto decoder = factory->createDecoder(file);
259 if(decoder) return std::move(decoder);
261 if(!file || !(file->clear(),file->seekg(0)))
262 return std::make_exception_ptr(
263 std::runtime_error("Failed to rewind file for the next decoder factory")
266 decoders = decoders.slice(1);
269 return alure::SharedPtr<alure::Decoder>(nullptr);
272 static alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> file)
274 auto decoder = GetDecoder(file, sDecoders);
275 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
276 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
277 decoder = GetDecoder(file, sDefaultDecoders);
278 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
279 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
280 return (decoder = std::make_exception_ptr(std::runtime_error("No decoder found")));
283 class DefaultFileIOFactory final : public alure::FileIOFactory {
284 alure::UniquePtr<std::istream> openFile(const alure::String &name) noexcept override
286 #ifdef _WIN32
287 auto file = alure::MakeUnique<Stream>(name.c_str());
288 #else
289 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
290 #endif
291 if(!file->is_open()) file = nullptr;
292 return std::move(file);
295 DefaultFileIOFactory sDefaultFileFactory;
297 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
301 namespace alure {
303 static inline void CheckContext(const ContextImpl *ctx)
305 auto count = ContextImpl::sContextSetCount.load(std::memory_order_acquire);
306 if(UNLIKELY(count != ctx->mContextSetCounter))
308 if(UNLIKELY(ctx != ContextImpl::GetCurrent()))
309 throw std::runtime_error("Called context is not current");
310 ctx->mContextSetCounter = count;
314 std::variant<std::monostate,uint64_t> ParseTimeval(StringView strval, double srate) noexcept
316 try {
317 size_t endpos;
318 size_t cpos = strval.find_first_of(':');
319 if(cpos == StringView::npos)
321 // No colon is present, treat it as a plain sample offset
322 uint64_t val = std::stoull(String(strval), &endpos);
323 if(endpos != strval.length()) return {};
324 return val;
327 // Value is not a sample offset. Its format is [[HH:]MM]:SS[.sss] (at
328 // least one colon must exist to be interpreted this way).
329 uint64_t val = 0;
331 if(cpos != 0)
333 // If a non-empty first value, parse it (may be hours or minutes)
334 val = std::stoul(String(strval.data(), cpos), &endpos);
335 if(endpos != cpos) return {};
338 strval = strval.substr(cpos+1);
339 cpos = strval.find_first_of(':');
340 if(cpos != StringView::npos)
342 // If a second colon is present, the first value was hours and this is
343 // minutes, otherwise the first value was minutes.
344 uint64_t val2 = 0;
346 if(cpos != 0)
348 val2 = std::stoul(String(strval.data(), cpos), &endpos);
349 if(endpos != cpos || val2 >= 60) return {};
352 // Combines hours and minutes into the full minute count
353 if(val > std::numeric_limits<uint64_t>::max()/60)
354 return {};
355 val = val*60 + val2;
356 strval = strval.substr(cpos+1);
359 double secs = 0.0;
360 if(!strval.empty())
362 // Parse the seconds and its fraction. Only include the first 3 decimal
363 // places for millisecond precision.
364 size_t dpos = strval.find_first_of('.');
365 String str = (dpos == StringView::npos) ?
366 String(strval) : String(strval.substr(0, dpos+4));
367 secs = std::stod(str, &endpos);
368 if(endpos != str.length() || !(secs >= 0.0 && secs < 60.0))
369 return {};
372 // Convert minutes to seconds, add the seconds, then convert to samples.
373 return static_cast<uint64_t>((val*60.0 + secs) * srate);
375 catch(...) {
378 return {};
382 Decoder::~Decoder() { }
383 DecoderFactory::~DecoderFactory() { }
385 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
387 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
388 [](const DecoderEntryPair &entry, StringView rhs) -> bool
389 { return entry.first < rhs; }
391 if(iter != sDecoders.end())
392 throw std::runtime_error("Decoder factory already registered");
393 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
396 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name) noexcept
398 UniquePtr<DecoderFactory> factory;
399 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
400 [](const DecoderEntryPair &entry, StringView rhs) noexcept -> bool
401 { return entry.first < rhs; }
403 if(iter != sDecoders.end())
405 factory = std::move(iter->second);
406 sDecoders.erase(iter);
407 return factory;
409 return factory;
413 FileIOFactory::~FileIOFactory() { }
415 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory) noexcept
417 sFileFactory.swap(factory);
418 return factory;
421 FileIOFactory &FileIOFactory::get() noexcept
423 FileIOFactory *factory = sFileFactory.get();
424 if(factory) return *factory;
425 return sDefaultFileFactory;
429 // Default message handler methods are no-ops.
430 MessageHandler::~MessageHandler()
434 void MessageHandler::deviceDisconnected(Device) noexcept
438 void MessageHandler::sourceStopped(Source) noexcept
442 void MessageHandler::sourceForceStopped(Source) noexcept
446 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>) noexcept
450 String MessageHandler::resourceNotFound(StringView) noexcept
452 return String();
456 template<typename T>
457 static inline void LoadALFunc(T **func, const char *name)
458 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
460 static void LoadNothing(ContextImpl*) { }
462 static void LoadEFX(ContextImpl *ctx)
464 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
465 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
466 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
467 LoadALFunc(&ctx->alEffecti, "alEffecti");
468 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
469 LoadALFunc(&ctx->alEffectf, "alEffectf");
470 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
471 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
472 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
473 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
474 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
476 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
477 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
478 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
479 LoadALFunc(&ctx->alFilteri, "alFilteri");
480 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
481 LoadALFunc(&ctx->alFilterf, "alFilterf");
482 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
483 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
484 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
485 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
486 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
488 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
489 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
490 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
491 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
492 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
493 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
494 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
495 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
496 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
497 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
498 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
501 static void LoadSourceResampler(ContextImpl *ctx)
503 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
506 static void LoadSourceLatency(ContextImpl *ctx)
508 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
509 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
512 static const struct {
513 AL extension;
514 const char name[32];
515 void (&loader)(ContextImpl*);
516 } ALExtensionList[] = {
517 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
519 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
520 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
521 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
523 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
524 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
525 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
527 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
528 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
529 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
530 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
532 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
534 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
535 { AL::EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
539 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
540 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
542 std::atomic<uint64_t> ContextImpl::sContextSetCount{0};
544 void ContextImpl::MakeCurrent(ContextImpl *context)
546 std::unique_lock<std::mutex> ctxlock(gGlobalCtxMutex);
548 if(alcMakeContextCurrent(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
549 throw std::runtime_error("Call to alcMakeContextCurrent failed");
550 if(context)
552 context->addRef();
553 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
555 std::swap(sCurrentCtx, context);
556 if(context) context->decRef();
558 if(sThreadCurrentCtx)
559 sThreadCurrentCtx->decRef();
560 sThreadCurrentCtx = nullptr;
561 sContextSetCount.fetch_add(1, std::memory_order_release);
563 if((context = sCurrentCtx) != nullptr)
565 ctxlock.unlock();
566 context->mWakeThread.notify_all();
570 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
572 if(!DeviceManagerImpl::SetThreadContext)
573 throw std::runtime_error("Thread-local contexts unsupported");
574 if(DeviceManagerImpl::SetThreadContext(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
575 throw std::runtime_error("Call to alcSetThreadContext failed");
576 if(context)
578 context->addRef();
579 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
581 if(sThreadCurrentCtx)
582 sThreadCurrentCtx->decRef();
583 sThreadCurrentCtx = context;
584 sContextSetCount.fetch_add(1, std::memory_order_release);
587 void ContextImpl::setupExts()
589 ALCdevice *device = mDevice.getALCdevice();
590 for(const auto &entry : ALExtensionList)
592 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
593 alIsExtensionPresent(entry.name))
595 mHasExt.set(static_cast<size_t>(entry.extension));
596 entry.loader(this);
602 void ContextImpl::backgroundProc()
604 if(DeviceManagerImpl::SetThreadContext && mDevice.hasExtension(ALC::EXT_thread_local_context))
605 DeviceManagerImpl::SetThreadContext(getALCcontext());
607 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
608 std::chrono::milliseconds waketime(0);
609 std::unique_lock<std::mutex> ctxlock(gGlobalCtxMutex);
610 while(!mQuitThread.load(std::memory_order_acquire))
613 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
614 mStreamingSources.erase(
615 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
616 [](SourceImpl *source) -> bool
617 { return !source->updateAsync(); }
618 ), mStreamingSources.end()
622 // Only do one pending buffer at a time. In case there's several large
623 // buffers to load, we still need to process streaming sources so they
624 // don't underrun.
625 PendingPromise *lastpb = mPendingCurrent.load(std::memory_order_acquire);
626 if(PendingPromise *pb = lastpb->mNext.load(std::memory_order_relaxed))
628 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
629 pb->mPromise.set_value(Buffer(pb->mBuffer));
630 Promise<Buffer>().swap(pb->mPromise);
631 mPendingCurrent.store(pb, std::memory_order_release);
632 continue;
635 std::unique_lock<std::mutex> wakelock(mWakeMutex);
636 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
638 ctxlock.unlock();
640 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
641 if(interval.count() == 0)
642 mWakeThread.wait(wakelock);
643 else
645 auto now = std::chrono::steady_clock::now() - basetime;
646 if(now > waketime)
648 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
649 waketime += interval * mult;
651 mWakeThread.wait_until(wakelock, waketime + basetime);
653 wakelock.unlock();
655 ctxlock.lock();
656 while(!mQuitThread.load(std::memory_order_acquire) &&
657 alcGetCurrentContext() != getALCcontext())
658 mWakeThread.wait(ctxlock);
661 ctxlock.unlock();
663 if(DeviceManagerImpl::SetThreadContext)
664 DeviceManagerImpl::SetThreadContext(nullptr);
668 ContextImpl::ContextImpl(DeviceImpl &device, ArrayView<AttributePair> attrs)
669 : mListener(this), mDevice(device), mIsConnected(true), mIsBatching(false)
671 ALCdevice *alcdev = mDevice.getALCdevice();
672 if(attrs.empty()) /* No explicit attributes. */
673 mContext.reset(alcCreateContext(alcdev, nullptr));
674 else
675 mContext.reset(alcCreateContext(alcdev, &attrs.front().mAttribute));
676 if(!mContext) throw alc_error(alcGetError(alcdev), "alcCreateContext failed");
678 mSourceIds.reserve(256);
679 mPendingTail = mPendingHead = new PendingPromise();
680 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
683 ContextImpl::~ContextImpl()
685 if(mThread.joinable())
687 std::unique_lock<std::mutex> lock(mWakeMutex);
688 mQuitThread.store(true, std::memory_order_relaxed);
689 lock.unlock();
690 mWakeThread.notify_all();
691 mThread.join();
694 PendingPromise *pb = mPendingTail;
695 while(pb)
697 PendingPromise *next = pb->mNext.load(std::memory_order_relaxed);
698 delete pb;
699 pb = next;
701 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
702 mPendingTail = mPendingHead = nullptr;
704 mEffectSlots.clear();
705 mEffects.clear();
707 std::lock_guard<std::mutex> ctxlock(gGlobalCtxMutex);
708 if(sCurrentCtx == this)
710 sCurrentCtx = nullptr;
711 sContextSetCount.fetch_add(1, std::memory_order_release);
713 if(sThreadCurrentCtx == this)
715 sThreadCurrentCtx = nullptr;
716 sContextSetCount.fetch_add(1, std::memory_order_release);
721 void Context::destroy()
723 ContextImpl *i = pImpl;
724 pImpl = nullptr;
725 i->destroy();
727 void ContextImpl::destroy()
729 if(mRefs != 0)
731 std::lock_guard<std::mutex> ctxlock(gGlobalCtxMutex);
732 if(!(mRefs == 1 && sCurrentCtx == this))
733 throw std::runtime_error("Context is in use");
734 decRef();
735 sCurrentCtx = nullptr;
736 sContextSetCount.fetch_add(1, std::memory_order_release);
739 if(mThread.joinable())
741 std::unique_lock<std::mutex> lock(mWakeMutex);
742 mQuitThread.store(true, std::memory_order_release);
743 lock.unlock();
744 mWakeThread.notify_all();
745 mThread.join();
748 std::unique_lock<std::mutex> lock(gGlobalCtxMutex);
749 if(UNLIKELY(alcMakeContextCurrent(getALCcontext()) == ALC_FALSE))
750 std::cerr<< "Failed to cleanup context!" <<std::endl;
751 else
753 mSourceGroups.clear();
754 mFreeSources.clear();
755 mAllSources.clear();
757 if(!mSourceIds.empty())
758 alDeleteSources(static_cast<ALsizei>(mSourceIds.size()), mSourceIds.data());
759 mSourceIds.clear();
761 for(auto &bufptr : mBuffers)
763 ALuint id = bufptr->getId();
764 alDeleteBuffers(1, &id);
766 mBuffers.clear();
768 mEffectSlots.clear();
769 mEffects.clear();
771 ALCcontext *alctx = sCurrentCtx ? sCurrentCtx->getALCcontext() : nullptr;
772 if(UNLIKELY(alcMakeContextCurrent(alctx) == ALC_FALSE))
773 std::cerr<< "Failed to reset global context!" <<std::endl;
774 if(ContextImpl *thrd_ctx = sThreadCurrentCtx)
776 // alcMakeContextCurrent sets the calling thread's context to null,
777 // set it back to what it was.
778 alctx = thrd_ctx->getALCcontext();
779 if(UNLIKELY(DeviceManagerImpl::SetThreadContext(alctx) == ALC_FALSE))
780 std::cerr<< "Failed to reset thread context!" <<std::endl;
783 lock.unlock();
785 mDevice.removeContext(this);
789 DECL_THUNK0(void, Context, startBatch,)
790 void ContextImpl::startBatch()
792 alcSuspendContext(mContext.get());
793 mIsBatching = true;
796 DECL_THUNK0(void, Context, endBatch,)
797 void ContextImpl::endBatch()
799 alcProcessContext(mContext.get());
800 mIsBatching = false;
804 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
805 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler>&& handler)
807 std::lock_guard<std::mutex> lock(gGlobalCtxMutex);
808 mMessage.swap(handler);
809 return handler;
813 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
814 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
816 if(interval.count() < 0 || interval > std::chrono::seconds(1))
817 throw std::out_of_range("Async wake interval out of range");
818 mWakeInterval.store(interval);
819 mWakeMutex.lock(); mWakeMutex.unlock();
820 mWakeThread.notify_all();
824 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
826 String oldname = String(name);
827 auto file = FileIOFactory::get().openFile(oldname);
828 if(UNLIKELY(!file))
830 // Resource not found. Try to find a substitute.
831 if(!mMessage.get())
832 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
833 do {
834 String newname(mMessage->resourceNotFound(oldname));
835 if(newname.empty())
836 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
837 file = FileIOFactory::get().openFile(newname);
838 oldname = std::move(newname);
839 } while(!file);
841 return GetDecoder(std::move(file));
844 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
845 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
847 CheckContext(this);
848 DecoderOrExceptT dec = findDecoder(name);
849 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
850 return std::move(*decoder);
851 std::rethrow_exception(std::get<std::exception_ptr>(dec));
855 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
856 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
858 CheckContext(this);
859 return GetFormat(channels, type) != AL_NONE;
863 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
864 ArrayView<String> ContextImpl::getAvailableResamplers()
866 CheckContext(this);
867 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
869 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
870 mResamplers.reserve(num_resamplers);
871 for(int i = 0;i < num_resamplers;i++)
872 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
873 if(mResamplers.empty())
874 mResamplers.emplace_back();
876 return mResamplers;
879 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
880 ALsizei ContextImpl::getDefaultResamplerIndex() const
882 CheckContext(this);
883 if(!hasExtension(AL::SOFT_source_resampler))
884 return 0;
885 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
889 ContextImpl::FutureBufferListT::const_iterator ContextImpl::findFutureBufferName(StringView name, size_t name_hash) const
891 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), name_hash,
892 [](const PendingBuffer &lhs, size_t rhs) -> bool
893 { return lhs.mBuffer->getNameHash() < rhs; }
895 while(iter != mFutureBuffers.end() && iter->mBuffer->getNameHash() == name_hash &&
896 iter->mBuffer->getName() != name)
897 ++iter;
898 return iter;
901 ContextImpl::BufferListT::const_iterator ContextImpl::findBufferName(StringView name, size_t name_hash) const
903 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), name_hash,
904 [](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
905 { return lhs->getNameHash() < rhs; }
907 while(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash &&
908 (*iter)->getName() != name)
909 ++iter;
910 return iter;
913 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, size_t name_hash, BufferListT::const_iterator iter, SharedPtr<Decoder> decoder)
915 ALuint srate = decoder->getFrequency();
916 ChannelConfig chans = decoder->getChannelConfig();
917 SampleType type = decoder->getSampleType();
918 ALuint frames = static_cast<ALuint>(
919 std::min<uint64_t>(decoder->getLength(), std::numeric_limits<ALuint>::max())
922 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
923 frames = decoder->read(data.data(), frames);
924 if(!frames)
925 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
926 data.resize(FramesToBytes(frames, chans, type));
928 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
929 if(loop_pts.first >= loop_pts.second)
930 loop_pts = std::make_pair(0, frames);
931 else
933 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
934 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
937 // Get the format before calling the bufferLoading message handler, to
938 // ensure it's something OpenAL can handle.
939 ALenum format = GetFormat(chans, type);
940 if(UNLIKELY(format == AL_NONE))
942 auto str = String("Unsupported format (")+GetSampleTypeName(type)+", "+
943 GetChannelConfigName(chans)+")";
944 return std::make_exception_ptr(std::runtime_error(str));
947 if(mMessage.get())
948 mMessage->bufferLoading(name, chans, type, srate, data);
950 alGetError();
951 ALuint bid = 0;
952 alGenBuffers(1, &bid);
953 alBufferData(bid, format, data.data(), static_cast<ALsizei>(data.size()), srate);
954 if(hasExtension(AL::SOFT_loop_points))
956 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
957 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
959 if(ALenum err = alGetError())
961 alDeleteBuffers(1, &bid);
962 return std::make_exception_ptr(al_error(err, "Failed to buffer data"));
965 return mBuffers.insert(iter,
966 MakeUnique<BufferImpl>(*this, bid, srate, chans, type, name, name_hash)
967 )->get();
970 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, size_t name_hash, BufferListT::const_iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
972 ALuint srate = decoder->getFrequency();
973 ChannelConfig chans = decoder->getChannelConfig();
974 SampleType type = decoder->getSampleType();
975 ALuint frames = static_cast<ALuint>(
976 std::min<uint64_t>(decoder->getLength(), std::numeric_limits<ALuint>::max())
978 if(!frames)
979 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
981 ALenum format = GetFormat(chans, type);
982 if(UNLIKELY(format == AL_NONE))
984 auto str = String("Unsupported format (")+GetSampleTypeName(type)+", "+
985 GetChannelConfigName(chans)+")";
986 return std::make_exception_ptr(std::runtime_error(str));
989 alGetError();
990 ALuint bid = 0;
991 alGenBuffers(1, &bid);
992 if(ALenum err = alGetError())
993 return std::make_exception_ptr(al_error(err, "Failed to create buffer"));
995 auto buffer = MakeUnique<BufferImpl>(*this, bid, srate, chans, type, name, name_hash);
997 if(mThread.get_id() == std::thread::id())
998 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1000 PendingPromise *pf = nullptr;
1001 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
1002 pf = new PendingPromise(buffer.get(), std::move(decoder), format, frames,
1003 std::move(promise));
1004 else
1006 pf = mPendingTail;
1007 pf->mBuffer = buffer.get();
1008 pf->mDecoder = std::move(decoder);
1009 pf->mFormat = format;
1010 pf->mFrames = frames;
1011 pf->mPromise = std::move(promise);
1012 mPendingTail = pf->mNext.exchange(nullptr, std::memory_order_relaxed);
1015 mPendingHead->mNext.store(pf, std::memory_order_release);
1016 mPendingHead = pf;
1018 return mBuffers.insert(iter, std::move(buffer))->get();
1021 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1022 Buffer ContextImpl::getBuffer(StringView name)
1024 CheckContext(this);
1026 auto hasher = std::hash<StringView>();
1027 size_t name_hash = hasher(name);
1028 if(UNLIKELY(!mFutureBuffers.empty()))
1030 Buffer buffer;
1032 // If the buffer is already pending for the future, wait for it
1033 auto iter = findFutureBufferName(name, name_hash);
1034 if(iter != mFutureBuffers.end() && iter->mBuffer->getNameHash() == name_hash)
1036 buffer = iter->mFuture.get();
1037 mFutureBuffers.erase(iter);
1040 // Clear out any completed futures.
1041 mFutureBuffers.erase(
1042 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1043 [](const PendingBuffer &entry) -> bool
1044 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1045 ), mFutureBuffers.end()
1048 // If we got the buffer, return it. Otherwise, go load it normally.
1049 if(buffer) return buffer;
1052 auto iter = findBufferName(name, name_hash);
1053 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1054 return Buffer(iter->get());
1056 BufferOrExceptT ret = doCreateBuffer(name, name_hash, iter, createDecoder(name));
1057 Buffer *buffer = std::get_if<Buffer>(&ret);
1058 if(UNLIKELY(!buffer))
1059 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1060 return *buffer;
1063 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1064 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
1066 SharedFuture<Buffer> future;
1067 CheckContext(this);
1069 auto hasher = std::hash<StringView>();
1070 size_t name_hash = hasher(name);
1071 if(UNLIKELY(!mFutureBuffers.empty()))
1073 // Check if the future that's being created already exists
1074 auto iter = findFutureBufferName(name, name_hash);
1075 if(iter != mFutureBuffers.end() && iter->mBuffer->getNameHash() == name_hash)
1077 future = iter->mFuture;
1078 if(GetFutureState(future) == std::future_status::ready)
1079 mFutureBuffers.erase(iter);
1080 return future;
1083 // Clear out any fulfilled futures.
1084 mFutureBuffers.erase(
1085 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1086 [](const PendingBuffer &entry) -> bool
1087 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1088 ), mFutureBuffers.end()
1092 auto iter = findBufferName(name, name_hash);
1093 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1095 // User asked to create a future buffer that's already loaded. Just
1096 // construct a promise, fulfill the promise immediately, then return a
1097 // shared future that's already set.
1098 Promise<Buffer> promise;
1099 promise.set_value(Buffer(iter->get()));
1100 future = promise.get_future().share();
1101 return future;
1104 Promise<Buffer> promise;
1105 future = promise.get_future().share();
1107 BufferOrExceptT ret = doCreateBufferAsync(name, name_hash, iter, createDecoder(name), std::move(promise));
1108 Buffer *buffer = std::get_if<Buffer>(&ret);
1109 if(UNLIKELY(!buffer))
1110 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1111 mWakeMutex.lock(); mWakeMutex.unlock();
1112 mWakeThread.notify_all();
1114 mFutureBuffers.insert(
1115 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), name_hash,
1116 [](const PendingBuffer &lhs, size_t rhs) -> bool
1117 { return lhs.mBuffer->getNameHash() < rhs; }
1118 ), { buffer->getHandle(), future }
1121 return future;
1124 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1125 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
1127 CheckContext(this);
1129 if(UNLIKELY(!mFutureBuffers.empty()))
1131 // Clear out any fulfilled futures.
1132 mFutureBuffers.erase(
1133 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1134 [](const PendingBuffer &entry) -> bool
1135 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1136 ), mFutureBuffers.end()
1140 auto hasher = std::hash<StringView>();
1141 for(const StringView name : names)
1143 size_t name_hash = hasher(name);
1145 // Check if the buffer that's being created already exists
1146 auto iter = findBufferName(name, name_hash);
1147 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1148 continue;
1150 DecoderOrExceptT dec = findDecoder(name);
1151 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
1152 if(!decoder) continue;
1154 Promise<Buffer> promise;
1155 SharedFuture<Buffer> future = promise.get_future().share();
1157 BufferOrExceptT buf = doCreateBufferAsync(name, name_hash, iter, std::move(*decoder),
1158 std::move(promise));
1159 Buffer *buffer = std::get_if<Buffer>(&buf);
1160 if(UNLIKELY(!buffer)) continue;
1162 mFutureBuffers.insert(
1163 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), name_hash,
1164 [](const PendingBuffer &lhs, size_t rhs) -> bool
1165 { return lhs.mBuffer->getNameHash() < rhs; }
1166 ), { buffer->getHandle(), future }
1169 mWakeMutex.lock(); mWakeMutex.unlock();
1170 mWakeThread.notify_all();
1173 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1174 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder>&& decoder)
1176 CheckContext(this);
1178 auto hasher = std::hash<StringView>();
1179 size_t name_hash = hasher(name);
1180 auto iter = findBufferName(name, name_hash);
1181 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1182 throw std::runtime_error("Buffer already exists");
1184 BufferOrExceptT ret = doCreateBuffer(name, name_hash, iter, std::move(decoder));
1185 Buffer *buffer = std::get_if<Buffer>(&ret);
1186 if(UNLIKELY(!buffer))
1187 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1188 return *buffer;
1191 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1192 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder>&& decoder)
1194 SharedFuture<Buffer> future;
1195 CheckContext(this);
1197 if(UNLIKELY(!mFutureBuffers.empty()))
1199 // Clear out any fulfilled futures.
1200 mFutureBuffers.erase(
1201 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1202 [](const PendingBuffer &entry) -> bool
1203 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1204 ), mFutureBuffers.end()
1208 auto hasher = std::hash<StringView>();
1209 size_t name_hash = hasher(name);
1210 auto iter = findBufferName(name, name_hash);
1211 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1212 throw std::runtime_error("Buffer already exists");
1214 Promise<Buffer> promise;
1215 future = promise.get_future().share();
1217 BufferOrExceptT ret = doCreateBufferAsync(name, name_hash, iter, std::move(decoder), std::move(promise));
1218 Buffer *buffer = std::get_if<Buffer>(&ret);
1219 if(UNLIKELY(!buffer))
1220 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1221 mWakeMutex.lock(); mWakeMutex.unlock();
1222 mWakeThread.notify_all();
1224 mFutureBuffers.insert(
1225 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), name_hash,
1226 [](const PendingBuffer &lhs, size_t rhs) -> bool
1227 { return lhs.mBuffer->getNameHash() < rhs; }
1228 ), { buffer->getHandle(), future }
1231 return future;
1235 DECL_THUNK1(Buffer, Context, findBuffer,, StringView)
1236 Buffer ContextImpl::findBuffer(StringView name)
1238 Buffer buffer;
1239 CheckContext(this);
1241 auto hasher = std::hash<StringView>();
1242 size_t name_hash = hasher(name);
1243 if(UNLIKELY(!mFutureBuffers.empty()))
1245 // If the buffer is already pending for the future, wait for it
1246 auto iter = findFutureBufferName(name, name_hash);
1247 if(iter != mFutureBuffers.end() && iter->mBuffer->getNameHash() == name_hash)
1249 buffer = iter->mFuture.get();
1250 mFutureBuffers.erase(iter);
1253 // Clear out any completed futures.
1254 mFutureBuffers.erase(
1255 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1256 [](const PendingBuffer &entry) -> bool
1257 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1258 ), mFutureBuffers.end()
1262 if(LIKELY(!buffer))
1264 auto iter = findBufferName(name, name_hash);
1265 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1266 buffer = Buffer(iter->get());
1268 return buffer;
1271 DECL_THUNK1(SharedFuture<Buffer>, Context, findBufferAsync,, StringView)
1272 SharedFuture<Buffer> ContextImpl::findBufferAsync(StringView name)
1274 SharedFuture<Buffer> future;
1275 CheckContext(this);
1277 auto hasher = std::hash<StringView>();
1278 size_t name_hash = hasher(name);
1279 if(UNLIKELY(!mFutureBuffers.empty()))
1281 // Check if the future that's being created already exists
1282 auto iter = findFutureBufferName(name, name_hash);
1283 if(iter != mFutureBuffers.end() && iter->mBuffer->getNameHash() == name_hash)
1285 future = iter->mFuture;
1286 if(GetFutureState(future) == std::future_status::ready)
1287 mFutureBuffers.erase(iter);
1288 return future;
1291 // Clear out any fulfilled futures.
1292 mFutureBuffers.erase(
1293 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1294 [](const PendingBuffer &entry) -> bool
1295 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1296 ), mFutureBuffers.end()
1300 auto iter = findBufferName(name, name_hash);
1301 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1303 // User asked to create a future buffer that's already loaded. Just
1304 // construct a promise, fulfill the promise immediately, then return a
1305 // shared future that's already set.
1306 Promise<Buffer> promise;
1307 promise.set_value(Buffer(iter->get()));
1308 future = promise.get_future().share();
1310 return future;
1314 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1315 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1316 void ContextImpl::removeBuffer(StringView name)
1318 CheckContext(this);
1320 auto hasher = std::hash<StringView>();
1321 size_t name_hash = hasher(name);
1322 if(UNLIKELY(!mFutureBuffers.empty()))
1324 // If the buffer is already pending for the future, wait for it to
1325 // finish before continuing.
1326 auto iter = findFutureBufferName(name, name_hash);
1327 if(iter != mFutureBuffers.end() && iter->mBuffer->getNameHash() == name_hash)
1329 iter->mFuture.wait();
1330 mFutureBuffers.erase(iter);
1333 // Clear out any completed futures.
1334 mFutureBuffers.erase(
1335 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1336 [](const PendingBuffer &entry) -> bool
1337 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1338 ), mFutureBuffers.end()
1342 auto iter = findBufferName(name, name_hash);
1343 if(iter != mBuffers.end() && (*iter)->getNameHash() == name_hash)
1345 // Remove pending sources whose future was waiting for this buffer.
1346 BufferImpl *buffer = iter->get();
1347 mPendingSources.erase(
1348 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1349 [buffer](PendingSource &entry) -> bool
1351 return (GetFutureState(entry.mFuture) == std::future_status::ready &&
1352 entry.mFuture.get().getHandle() == buffer);
1354 ), mPendingSources.end()
1356 (*iter)->cleanup();
1357 mBuffers.erase(iter);
1362 ALuint ContextImpl::getSourceId(ALuint maxprio)
1364 ALuint id = 0;
1365 if(mSourceIds.empty())
1367 alGetError();
1368 alGenSources(1, &id);
1369 if(alGetError() == AL_NO_ERROR)
1370 return id;
1372 SourceImpl *lowest = nullptr;
1373 for(SourceBufferUpdateEntry &entry : mPlaySources)
1375 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1376 lowest = entry.mSource;
1378 for(SourceStreamUpdateEntry &entry : mStreamSources)
1380 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1381 lowest = entry.mSource;
1383 if(lowest && lowest->getPriority() < maxprio)
1385 lowest->stop();
1386 if(mMessage.get())
1387 mMessage->sourceForceStopped(lowest);
1390 if(mSourceIds.empty())
1391 throw std::runtime_error("No available sources");
1393 id = mSourceIds.back();
1394 mSourceIds.pop_back();
1395 return id;
1399 DECL_THUNK0(Source, Context, createSource,)
1400 Source ContextImpl::createSource()
1402 CheckContext(this);
1404 SourceImpl *source;
1405 if(!mFreeSources.empty())
1407 source = mFreeSources.back();
1408 mFreeSources.pop_back();
1410 else
1412 mAllSources.emplace_back(*this);
1413 source = &mAllSources.back();
1415 return Source(source);
1419 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1421 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1422 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1423 { return lhs.mSource < rhs; }
1425 if(iter != mPendingSources.end() && iter->mSource == source)
1426 iter->mFuture = std::move(future);
1427 else
1428 mPendingSources.insert(iter, {source, std::move(future)});
1431 void ContextImpl::removePendingSource(SourceImpl *source)
1433 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1434 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1435 { return lhs.mSource < rhs; }
1437 if(iter != mPendingSources.end() && iter->mSource == source)
1438 mPendingSources.erase(iter);
1441 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1443 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1444 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1445 { return lhs.mSource < rhs; }
1447 return (iter != mPendingSources.end() && iter->mSource == source);
1450 void ContextImpl::addFadingSource(SourceImpl *source, std::chrono::nanoseconds duration, ALfloat gain)
1452 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1453 [](const SourceFadeUpdateEntry &lhs, SourceImpl *rhs) -> bool
1454 { return lhs.mSource < rhs; }
1456 if(iter == mFadingSources.end() || iter->mSource != source)
1458 auto now = mDevice.getClockTime();
1459 mFadingSources.emplace(iter, SourceFadeUpdateEntry{source, now, now+duration, true, gain});
1463 void ContextImpl::removeFadingSource(SourceImpl *source)
1465 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1466 [](const SourceFadeUpdateEntry &lhs, SourceImpl *rhs) -> bool
1467 { return lhs.mSource < rhs; }
1469 if(iter != mFadingSources.end() && iter->mSource == source)
1470 mFadingSources.erase(iter);
1473 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1475 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1476 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1477 { return lhs.mSource < rhs; }
1479 if(iter == mPlaySources.end() || iter->mSource != source)
1480 mPlaySources.insert(iter, {source,id});
1483 void ContextImpl::addPlayingSource(SourceImpl *source)
1485 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1486 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1487 { return lhs.mSource < rhs; }
1489 if(iter == mStreamSources.end() || iter->mSource != source)
1490 mStreamSources.insert(iter, {source});
1493 void ContextImpl::removePlayingSource(SourceImpl *source)
1495 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1496 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1497 { return lhs.mSource < rhs; }
1499 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1500 mPlaySources.erase(iter0);
1501 else
1503 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1504 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1505 { return lhs.mSource < rhs; }
1507 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1508 mStreamSources.erase(iter1);
1513 void ContextImpl::addStream(SourceImpl *source)
1515 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1516 if(mThread.get_id() == std::thread::id())
1517 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1518 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1519 if(iter == mStreamingSources.end() || *iter != source)
1520 mStreamingSources.insert(iter, source);
1523 void ContextImpl::removeStream(SourceImpl *source)
1525 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1526 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1527 if(iter != mStreamingSources.end() && *iter == source)
1528 mStreamingSources.erase(iter);
1531 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1533 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1534 if(iter != mStreamingSources.end() && *iter == source)
1535 mStreamingSources.erase(iter);
1539 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1540 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1542 if(!hasExtension(AL::EXT_EFX))
1543 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1544 CheckContext(this);
1546 auto slot = MakeUnique<AuxiliaryEffectSlotImpl>(*this);
1547 auto iter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), slot);
1548 return AuxiliaryEffectSlot(mEffectSlots.insert(iter, std::move(slot))->get());
1551 void ContextImpl::freeEffectSlot(AuxiliaryEffectSlotImpl *slot)
1553 auto iter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), slot,
1554 [](const UniquePtr<AuxiliaryEffectSlotImpl> &lhs, AuxiliaryEffectSlotImpl *rhs) -> bool
1555 { return lhs.get() < rhs; }
1557 if(iter != mEffectSlots.end() && iter->get() == slot)
1558 mEffectSlots.erase(iter);
1562 DECL_THUNK0(Effect, Context, createEffect,)
1563 Effect ContextImpl::createEffect()
1565 if(!hasExtension(AL::EXT_EFX))
1566 throw std::runtime_error("Effects not supported");
1567 CheckContext(this);
1569 auto effect = MakeUnique<EffectImpl>(*this);
1570 auto iter = std::lower_bound(mEffects.begin(), mEffects.end(), effect);
1571 return Effect(mEffects.insert(iter, std::move(effect))->get());
1574 void ContextImpl::freeEffect(EffectImpl *effect)
1576 auto iter = std::lower_bound(mEffects.begin(), mEffects.end(), effect,
1577 [](const UniquePtr<EffectImpl> &lhs, EffectImpl *rhs) -> bool
1578 { return lhs.get() < rhs; }
1580 if(iter != mEffects.end() && iter->get() == effect)
1581 mEffects.erase(iter);
1585 DECL_THUNK0(SourceGroup, Context, createSourceGroup,)
1586 SourceGroup ContextImpl::createSourceGroup()
1588 auto srcgroup = MakeUnique<SourceGroupImpl>(*this);
1589 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), srcgroup);
1591 iter = mSourceGroups.insert(iter, std::move(srcgroup));
1592 return SourceGroup(iter->get());
1595 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1597 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group,
1598 [](const UniquePtr<SourceGroupImpl> &lhs, SourceGroupImpl *rhs) -> bool
1599 { return lhs.get() < rhs; }
1601 if(iter != mSourceGroups.end() && iter->get() == group)
1602 mSourceGroups.erase(iter);
1606 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1607 void ContextImpl::setDopplerFactor(ALfloat factor)
1609 if(!(factor >= 0.0f))
1610 throw std::out_of_range("Doppler factor out of range");
1611 CheckContext(this);
1612 alDopplerFactor(factor);
1616 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1617 void ContextImpl::setSpeedOfSound(ALfloat speed)
1619 if(!(speed > 0.0f))
1620 throw std::out_of_range("Speed of sound out of range");
1621 CheckContext(this);
1622 alSpeedOfSound(speed);
1626 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1627 void ContextImpl::setDistanceModel(DistanceModel model)
1629 CheckContext(this);
1630 alDistanceModel((ALenum)model);
1634 DECL_THUNK0(void, Context, update,)
1635 void ContextImpl::update()
1637 CheckContext(this);
1638 mPendingSources.erase(
1639 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1640 [](PendingSource &entry) -> bool
1641 { return !entry.mSource->checkPending(entry.mFuture); }
1642 ), mPendingSources.end()
1644 if(!mFadingSources.empty())
1646 auto cur_time = mDevice.getClockTime();
1647 mFadingSources.erase(
1648 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1649 [cur_time](SourceFadeUpdateEntry &entry) -> bool
1650 { return !entry.mSource->fadeUpdate(cur_time, entry); }
1651 ), mFadingSources.end()
1654 mPlaySources.erase(
1655 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1656 [](const SourceBufferUpdateEntry &entry) -> bool
1657 { return !entry.mSource->playUpdate(entry.mId); }
1658 ), mPlaySources.end()
1660 mStreamSources.erase(
1661 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1662 [](const SourceStreamUpdateEntry &entry) -> bool
1663 { return !entry.mSource->playUpdate(); }
1664 ), mStreamSources.end()
1667 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1669 // For performance reasons, don't wait for the thread's mutex. This
1670 // should be called often enough to keep up with any and all streams
1671 // regardless.
1672 mWakeThread.notify_all();
1675 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1677 ALCint connected;
1678 alcGetIntegerv(mDevice.getALCdevice(), ALC_CONNECTED, 1, &connected);
1679 mIsConnected = static_cast<bool>(connected);
1680 if(!mIsConnected && mMessage.get()) mMessage->deviceDisconnected(Device(&mDevice));
1684 DECL_THUNK0(Device, Context, getDevice,)
1685 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1686 DECL_THUNK0(Listener, Context, getListener,)
1687 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1689 void Context::MakeCurrent(Context context)
1690 { ContextImpl::MakeCurrent(context.pImpl); }
1692 Context Context::GetCurrent()
1693 { return Context(ContextImpl::GetCurrent()); }
1695 void Context::MakeThreadCurrent(Context context)
1696 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1698 Context Context::GetThreadCurrent()
1699 { return Context(ContextImpl::GetThreadCurrent()); }
1702 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1703 void ListenerImpl::setGain(ALfloat gain)
1705 if(!(gain >= 0.0f))
1706 throw std::out_of_range("Gain out of range");
1707 CheckContext(mContext);
1708 alListenerf(AL_GAIN, gain);
1712 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1713 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1715 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1716 CheckContext(mContext);
1717 Batcher batcher = mContext->getBatcher();
1718 alListenerfv(AL_POSITION, position.getPtr());
1719 alListenerfv(AL_VELOCITY, velocity.getPtr());
1720 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1723 DECL_THUNK1(void, Listener, setPosition,, const Vector3&)
1724 void ListenerImpl::setPosition(const Vector3 &position)
1726 CheckContext(mContext);
1727 alListenerfv(AL_POSITION, position.getPtr());
1730 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1731 void ListenerImpl::setPosition(const ALfloat *pos)
1733 CheckContext(mContext);
1734 alListenerfv(AL_POSITION, pos);
1737 DECL_THUNK1(void, Listener, setVelocity,, const Vector3&)
1738 void ListenerImpl::setVelocity(const Vector3 &velocity)
1740 CheckContext(mContext);
1741 alListenerfv(AL_VELOCITY, velocity.getPtr());
1744 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1745 void ListenerImpl::setVelocity(const ALfloat *vel)
1747 CheckContext(mContext);
1748 alListenerfv(AL_VELOCITY, vel);
1751 DECL_THUNK1(void, Listener, setOrientation,, const Vector3Pair&)
1752 void ListenerImpl::setOrientation(const std::pair<Vector3,Vector3> &orientation)
1754 CheckContext(mContext);
1755 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1758 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1759 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1761 CheckContext(mContext);
1762 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1763 alListenerfv(AL_ORIENTATION, ori);
1766 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1767 void ListenerImpl::setOrientation(const ALfloat *ori)
1769 CheckContext(mContext);
1770 alListenerfv(AL_ORIENTATION, ori);
1773 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)
1774 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1776 if(!(m_u > 0.0f))
1777 throw std::out_of_range("Invalid meters per unit");
1778 CheckContext(mContext);
1779 if(mContext->hasExtension(AL::EXT_EFX))
1780 alListenerf(AL_METERS_PER_UNIT, m_u);