Make sure the background thread is done when destructing the context
[alure.git] / src / context.cpp
blobedca7a2c22d2df746559c3b93ff7ec10aba37d90
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_LIBFLAC
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_MPG123
34 #include "decoders/mpg123.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
53 // Implements a FNV-1a hash for StringView. NOTE: This is *NOT* guaranteed
54 // compatible with std::hash<String>! The standard does not give any specific
55 // hash implementation, nor a way for applications to access the same hash
56 // function as std::string (short of copying into a string and hashing that).
57 // So if you need Strings and StringViews to result in the same hash for the
58 // same set of characters, hash StringViews created from the Strings.
59 template<>
60 struct hash<alure::StringView> {
61 size_t operator()(const alure::StringView &str) const noexcept
63 using traits_type = alure::StringView::traits_type;
65 if /*constexpr*/ (sizeof(size_t) == 8)
67 static constexpr size_t hash_offset = 0xcbf29ce484222325;
68 static constexpr size_t hash_prime = 0x100000001b3;
70 size_t val = hash_offset;
71 for(auto ch : str)
72 val = (val^traits_type::to_int_type(ch)) * hash_prime;
73 return val;
75 else
77 static constexpr size_t hash_offset = 0x811c9dc5;
78 static constexpr size_t hash_prime = 0x1000193;
80 size_t val = hash_offset;
81 for(auto ch : str)
82 val = (val^traits_type::to_int_type(ch)) * hash_prime;
83 return val;
90 namespace
93 // Global mutex to protect global context changes
94 std::mutex mGlobalCtxMutex;
96 #ifdef _WIN32
97 // Windows' std::ifstream fails with non-ANSI paths since the standard only
98 // specifies names using const char* (or std::string). MSVC has a non-standard
99 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
100 // but not all Windows compilers support it. So we have to make our own istream
101 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
102 class StreamBuf final : public std::streambuf {
103 alure::Array<char_type,4096> mBuffer;
104 HANDLE mFile{INVALID_HANDLE_VALUE};
106 int_type underflow() override
108 if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
110 // Read in the next chunk of data, and set the pointers on success
111 DWORD got = 0;
112 if(ReadFile(mFile, mBuffer.data(), mBuffer.size(), &got, NULL))
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 noexcept { return mFile != INVALID_HANDLE_VALUE; }
204 StreamBuf() = default;
205 ~StreamBuf() override
207 if(mFile != INVALID_HANDLE_VALUE)
208 CloseHandle(mFile);
209 mFile = INVALID_HANDLE_VALUE;
213 // Inherit from std::istream to use our custom streambuf
214 class Stream final : public std::istream {
215 public:
216 Stream(const char *filename) : std::istream(new StreamBuf())
218 // Set the failbit if the file failed to open.
219 if(!(static_cast<StreamBuf*>(rdbuf())->open(filename)))
220 clear(failbit);
222 ~Stream() override
223 { delete rdbuf(); }
225 bool is_open() const noexcept { return static_cast<StreamBuf*>(rdbuf())->is_open(); }
227 #endif
229 using DecoderEntryPair = std::pair<alure::String,alure::UniquePtr<alure::DecoderFactory>>;
230 const DecoderEntryPair sDefaultDecoders[] = {
231 #ifdef HAVE_WAVE
232 { "_alure_int_wave", alure::MakeUnique<alure::WaveDecoderFactory>() },
233 #endif
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 alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> &file,
254 alure::ArrayView<DecoderEntryPair> decoders)
256 while(!decoders.empty())
258 alure::DecoderFactory *factory = decoders.front().second.get();
259 auto decoder = factory->createDecoder(file);
260 if(decoder) return std::move(decoder);
262 if(!file || !(file->clear(),file->seekg(0)))
263 return std::make_exception_ptr(
264 std::runtime_error("Failed to rewind file for the next decoder factory")
267 decoders = decoders.slice(1);
270 return alure::SharedPtr<alure::Decoder>(nullptr);
273 static alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> file)
275 auto decoder = GetDecoder(file, sDecoders);
276 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
277 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
278 decoder = GetDecoder(file, sDefaultDecoders);
279 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
280 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
281 return (decoder = std::make_exception_ptr(std::runtime_error("No decoder found")));
284 class DefaultFileIOFactory final : public alure::FileIOFactory {
285 alure::UniquePtr<std::istream> openFile(const alure::String &name) noexcept 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 std::variant<std::monostate,uint64_t> ParseTimeval(StringView strval, double srate) noexcept
307 try {
308 size_t endpos;
309 size_t cpos = strval.find_first_of(':');
310 if(cpos == StringView::npos)
312 // No colon is present, treat it as a plain sample offset
313 uint64_t val = std::stoull(String(strval), &endpos);
314 if(endpos != strval.length()) return {};
315 return val;
318 // Value is not a sample offset. Its format is [[HH:]MM]:SS[.sss] (at
319 // least one colon must exist to be interpreted this way).
320 uint64_t val = 0;
322 if(cpos != 0)
324 // If a non-empty first value, parse it (may be hours or minutes)
325 val = std::stoul(String(strval.data(), cpos), &endpos);
326 if(endpos != cpos) return {};
329 strval = strval.substr(cpos+1);
330 cpos = strval.find_first_of(':');
331 if(cpos != StringView::npos)
333 // If a second colon is present, the first value was hours and this is
334 // minutes, otherwise the first value was minutes.
335 uint64_t val2 = 0;
337 if(cpos != 0)
339 val2 = std::stoul(String(strval.data(), cpos), &endpos);
340 if(endpos != cpos || val2 >= 60) return {};
343 // Combines hours and minutes into the full minute count
344 if(val > std::numeric_limits<uint64_t>::max()/60)
345 return {};
346 val = val*60 + val2;
347 strval = strval.substr(cpos+1);
350 double secs = 0.0;
351 if(!strval.empty())
353 // Parse the seconds and its fraction. Only include the first 3 decimal
354 // places for millisecond precision.
355 size_t dpos = strval.find_first_of('.');
356 String str = (dpos == StringView::npos) ?
357 String(strval) : String(strval.substr(0, dpos+4));
358 secs = std::stod(str, &endpos);
359 if(endpos != str.length() || !(secs >= 0.0 && secs < 60.0))
360 return {};
363 // Convert minutes to seconds, add the seconds, then convert to samples.
364 return static_cast<uint64_t>((val*60.0 + secs) * srate);
366 catch(...) {
369 return {};
373 Decoder::~Decoder() { }
374 DecoderFactory::~DecoderFactory() { }
376 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
378 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
379 [](const DecoderEntryPair &entry, StringView rhs) -> bool
380 { return entry.first < rhs; }
382 if(iter != sDecoders.end())
383 throw std::runtime_error("Decoder factory already registered");
384 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
387 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name) noexcept
389 UniquePtr<DecoderFactory> factory;
390 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
391 [](const DecoderEntryPair &entry, StringView rhs) noexcept -> bool
392 { return entry.first < rhs; }
394 if(iter != sDecoders.end())
396 factory = std::move(iter->second);
397 sDecoders.erase(iter);
398 return factory;
400 return factory;
404 FileIOFactory::~FileIOFactory() { }
406 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory) noexcept
408 sFileFactory.swap(factory);
409 return factory;
412 FileIOFactory &FileIOFactory::get() noexcept
414 FileIOFactory *factory = sFileFactory.get();
415 if(factory) return *factory;
416 return sDefaultFileFactory;
420 // Default message handler methods are no-ops.
421 MessageHandler::~MessageHandler()
425 void MessageHandler::deviceDisconnected(Device) noexcept
429 void MessageHandler::sourceStopped(Source) noexcept
433 void MessageHandler::sourceForceStopped(Source) noexcept
437 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>) noexcept
441 String MessageHandler::resourceNotFound(StringView) noexcept
443 return String();
447 template<typename T>
448 static inline void LoadALFunc(T **func, const char *name)
449 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
451 static void LoadNothing(ContextImpl*) { }
453 static void LoadEFX(ContextImpl *ctx)
455 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
456 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
457 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
458 LoadALFunc(&ctx->alEffecti, "alEffecti");
459 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
460 LoadALFunc(&ctx->alEffectf, "alEffectf");
461 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
462 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
463 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
464 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
465 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
467 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
468 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
469 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
470 LoadALFunc(&ctx->alFilteri, "alFilteri");
471 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
472 LoadALFunc(&ctx->alFilterf, "alFilterf");
473 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
474 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
475 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
476 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
477 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
479 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
480 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
481 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
482 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
483 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
484 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
485 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
486 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
487 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
488 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
489 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
492 static void LoadSourceResampler(ContextImpl *ctx)
494 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
497 static void LoadSourceLatency(ContextImpl *ctx)
499 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
500 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
503 static const struct {
504 enum AL extension;
505 const char name[32];
506 void (&loader)(ContextImpl*);
507 } ALExtensionList[] = {
508 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
510 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
511 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
512 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
514 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
515 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
516 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
518 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
519 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
520 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
521 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
523 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
525 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
526 { AL::EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
530 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
531 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
533 std::atomic<uint64_t> ContextImpl::sContextSetCount{0};
535 void ContextImpl::MakeCurrent(ContextImpl *context)
537 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
539 if(alcMakeContextCurrent(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
540 throw std::runtime_error("Call to alcMakeContextCurrent failed");
541 if(context)
543 context->addRef();
544 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
546 std::swap(sCurrentCtx, context);
547 if(context) context->decRef();
549 if(sThreadCurrentCtx)
550 sThreadCurrentCtx->decRef();
551 sThreadCurrentCtx = nullptr;
552 sContextSetCount.fetch_add(1, std::memory_order_release);
554 if((context = sCurrentCtx) != nullptr)
556 ctxlock.unlock();
557 context->mWakeThread.notify_all();
561 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
563 if(!DeviceManagerImpl::SetThreadContext)
564 throw std::runtime_error("Thread-local contexts unsupported");
565 if(DeviceManagerImpl::SetThreadContext(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
566 throw std::runtime_error("Call to alcSetThreadContext failed");
567 if(context)
569 context->addRef();
570 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
572 if(sThreadCurrentCtx)
573 sThreadCurrentCtx->decRef();
574 sThreadCurrentCtx = context;
575 sContextSetCount.fetch_add(1, std::memory_order_release);
578 void ContextImpl::setupExts()
580 ALCdevice *device = mDevice->getALCdevice();
581 mHasExt.clear();
582 for(const auto &entry : ALExtensionList)
584 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
585 alIsExtensionPresent(entry.name))
587 mHasExt.set(static_cast<size_t>(entry.extension));
588 entry.loader(this);
594 void ContextImpl::backgroundProc()
596 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(ALC::EXT_thread_local_context))
597 DeviceManagerImpl::SetThreadContext(getALCcontext());
599 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
600 std::chrono::milliseconds waketime(0);
601 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
602 while(!mQuitThread.load(std::memory_order_acquire))
605 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
606 mStreamingSources.erase(
607 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
608 [](SourceImpl *source) -> bool
609 { return !source->updateAsync(); }
610 ), mStreamingSources.end()
614 // Only do one pending buffer at a time. In case there's several large
615 // buffers to load, we still need to process streaming sources so they
616 // don't underrun.
617 PendingPromise *lastpb = mPendingCurrent.load(std::memory_order_acquire);
618 if(PendingPromise *pb = lastpb->mNext.load(std::memory_order_relaxed))
620 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
621 pb->mPromise.set_value(Buffer(pb->mBuffer));
622 Promise<Buffer>().swap(pb->mPromise);
623 mPendingCurrent.store(pb, std::memory_order_release);
624 continue;
627 std::unique_lock<std::mutex> wakelock(mWakeMutex);
628 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
630 ctxlock.unlock();
632 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
633 if(interval.count() == 0)
634 mWakeThread.wait(wakelock);
635 else
637 auto now = std::chrono::steady_clock::now() - basetime;
638 if(now > waketime)
640 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
641 waketime += interval * mult;
643 mWakeThread.wait_until(wakelock, waketime + basetime);
645 wakelock.unlock();
647 ctxlock.lock();
648 while(!mQuitThread.load(std::memory_order_acquire) &&
649 alcGetCurrentContext() != getALCcontext())
650 mWakeThread.wait(ctxlock);
653 ctxlock.unlock();
655 if(DeviceManagerImpl::SetThreadContext)
656 DeviceManagerImpl::SetThreadContext(nullptr);
660 ContextImpl::ContextImpl(DeviceImpl *device, ArrayView<AttributePair> attrs)
661 : mListener(this), mDevice(device), mIsConnected(true), mIsBatching(false)
663 ALCdevice *alcdev = mDevice->getALCdevice();
664 if(attrs.empty()) /* No explicit attributes. */
665 mContext = alcCreateContext(alcdev, nullptr);
666 else
667 mContext = alcCreateContext(alcdev, &std::get<0>(attrs.front()));
668 if(!mContext) throw alc_error(alcGetError(alcdev), "alcCreateContext failed");
670 mHasExt.clear();
671 mSourceIds.reserve(256);
672 mPendingHead = new PendingPromise{nullptr, {}, AL_NONE, 0, {}, {nullptr}};
673 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
674 mPendingTail = mPendingHead;
677 ContextImpl::~ContextImpl()
679 if(mThread.joinable())
681 std::unique_lock<std::mutex> lock(mWakeMutex);
682 mQuitThread.store(true, std::memory_order_relaxed);
683 lock.unlock();
684 mWakeThread.notify_all();
685 mThread.join();
688 PendingPromise *pb = mPendingTail;
689 while(pb)
691 PendingPromise *next = pb->mNext.load(std::memory_order_relaxed);
692 delete pb;
693 pb = next;
695 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
696 mPendingHead = nullptr;
697 mPendingTail = nullptr;
699 if(mContext)
700 alcDestroyContext(mContext);
701 mContext = nullptr;
705 void Context::destroy()
707 ContextImpl *i = pImpl;
708 pImpl = nullptr;
709 i->destroy();
711 void ContextImpl::destroy()
713 if(mRefs != 0)
714 throw std::runtime_error("Context is in use");
715 if(!mBuffers.empty())
716 throw std::runtime_error("Trying to destroy a context with buffers");
718 if(mThread.joinable())
720 std::unique_lock<std::mutex> lock(mWakeMutex);
721 mQuitThread.store(true, std::memory_order_release);
722 lock.unlock();
723 mWakeThread.notify_all();
724 mThread.join();
727 alcDestroyContext(mContext);
728 mContext = nullptr;
730 mDevice->removeContext(this);
734 DECL_THUNK0(void, Context, startBatch,)
735 void ContextImpl::startBatch()
737 alcSuspendContext(mContext);
738 mIsBatching = true;
741 DECL_THUNK0(void, Context, endBatch,)
742 void ContextImpl::endBatch()
744 alcProcessContext(mContext);
745 mIsBatching = false;
749 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
750 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler>&& handler)
752 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
753 mMessage.swap(handler);
754 return handler;
758 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
759 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
761 if(interval.count() < 0 || interval > std::chrono::seconds(1))
762 throw std::out_of_range("Async wake interval out of range");
763 mWakeInterval.store(interval);
764 mWakeMutex.lock(); mWakeMutex.unlock();
765 mWakeThread.notify_all();
769 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
771 String oldname = String(name);
772 auto file = FileIOFactory::get().openFile(oldname);
773 if(UNLIKELY(!file))
775 // Resource not found. Try to find a substitute.
776 if(!mMessage.get())
777 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
778 do {
779 String newname(mMessage->resourceNotFound(oldname));
780 if(newname.empty())
781 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
782 file = FileIOFactory::get().openFile(newname);
783 oldname = std::move(newname);
784 } while(!file);
786 return GetDecoder(std::move(file));
789 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
790 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
792 CheckContext(this);
793 DecoderOrExceptT dec = findDecoder(name);
794 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
795 return std::move(*decoder);
796 std::rethrow_exception(std::get<std::exception_ptr>(dec));
800 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
801 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
803 CheckContext(this);
804 return GetFormat(channels, type) != AL_NONE;
808 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
809 ArrayView<String> ContextImpl::getAvailableResamplers()
811 CheckContext(this);
812 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
814 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
815 mResamplers.reserve(num_resamplers);
816 for(int i = 0;i < num_resamplers;i++)
817 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
818 if(mResamplers.empty())
819 mResamplers.emplace_back();
821 return mResamplers;
824 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
825 ALsizei ContextImpl::getDefaultResamplerIndex() const
827 CheckContext(this);
828 if(!hasExtension(AL::SOFT_source_resampler))
829 return 0;
830 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
834 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
836 ALuint srate = decoder->getFrequency();
837 ChannelConfig chans = decoder->getChannelConfig();
838 SampleType type = decoder->getSampleType();
839 ALuint frames = decoder->getLength();
841 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
842 frames = decoder->read(data.data(), frames);
843 if(!frames)
844 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
845 data.resize(FramesToBytes(frames, chans, type));
847 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
848 if(loop_pts.first >= loop_pts.second)
849 loop_pts = std::make_pair(0, frames);
850 else
852 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
853 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
856 // Get the format before calling the bufferLoading message handler, to
857 // ensure it's something OpenAL can handle.
858 ALenum format = GetFormat(chans, type);
859 if(UNLIKELY(format == AL_NONE))
861 auto str = String("Unsupported format (")+GetSampleTypeName(type)+", "+
862 GetChannelConfigName(chans)+")";
863 return std::make_exception_ptr(std::runtime_error(str));
866 if(mMessage.get())
867 mMessage->bufferLoading(name, chans, type, srate, data);
869 alGetError();
870 ALuint bid = 0;
871 alGenBuffers(1, &bid);
872 alBufferData(bid, format, data.data(), data.size(), srate);
873 if(hasExtension(AL::SOFT_loop_points))
875 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
876 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
878 if(ALenum err = alGetError())
880 alDeleteBuffers(1, &bid);
881 return std::make_exception_ptr(al_error(err, "Failed to buffer data"));
884 return mBuffers.insert(iter,
885 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
886 )->get();
889 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
891 ALuint srate = decoder->getFrequency();
892 ChannelConfig chans = decoder->getChannelConfig();
893 SampleType type = decoder->getSampleType();
894 ALuint frames = decoder->getLength();
895 if(!frames)
896 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
898 ALenum format = GetFormat(chans, type);
899 if(UNLIKELY(format == AL_NONE))
901 auto str = String("Unsupported format (")+GetSampleTypeName(type)+", "+
902 GetChannelConfigName(chans)+")";
903 return std::make_exception_ptr(std::runtime_error(str));
906 alGetError();
907 ALuint bid = 0;
908 alGenBuffers(1, &bid);
909 if(ALenum err = alGetError())
910 return std::make_exception_ptr(al_error(err, "Failed to create buffer"));
912 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
914 if(mThread.get_id() == std::thread::id())
915 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
917 PendingPromise *pf = nullptr;
918 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
919 pf = new PendingPromise{buffer.get(), std::move(decoder), format, frames,
920 std::move(promise), {nullptr}};
921 else
923 pf = mPendingTail;
924 pf->mBuffer = buffer.get();
925 pf->mDecoder = std::move(decoder);
926 pf->mFormat = format;
927 pf->mFrames = frames;
928 pf->mPromise = std::move(promise);
929 mPendingTail = pf->mNext.exchange(nullptr, std::memory_order_relaxed);
932 mPendingHead->mNext.store(pf, std::memory_order_release);
933 mPendingHead = pf;
935 return mBuffers.insert(iter, std::move(buffer))->get();
938 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
939 Buffer ContextImpl::getBuffer(StringView name)
941 CheckContext(this);
943 auto hasher = std::hash<StringView>();
944 if(UNLIKELY(!mFutureBuffers.empty()))
946 Buffer buffer;
948 // If the buffer is already pending for the future, wait for it
949 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
950 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
951 { return hasher(lhs.mBuffer->getName()) < rhs; }
953 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
955 buffer = iter->mFuture.get();
956 mFutureBuffers.erase(iter);
959 // Clear out any completed futures.
960 mFutureBuffers.erase(
961 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
962 [](const PendingBuffer &entry) -> bool
963 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
964 ), mFutureBuffers.end()
967 // If we got the buffer, return it. Otherwise, go load it normally.
968 if(buffer) return buffer;
971 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
972 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
973 { return hasher(lhs->getName()) < rhs; }
975 if(iter != mBuffers.end() && (*iter)->getName() == name)
976 return Buffer(iter->get());
978 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
979 Buffer *buffer = std::get_if<Buffer>(&ret);
980 if(UNLIKELY(!buffer))
981 std::rethrow_exception(std::get<std::exception_ptr>(ret));
982 return *buffer;
985 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
986 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
988 SharedFuture<Buffer> future;
989 CheckContext(this);
991 auto hasher = std::hash<StringView>();
992 if(UNLIKELY(!mFutureBuffers.empty()))
994 // Check if the future that's being created already exists
995 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
996 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
997 { return hasher(lhs.mBuffer->getName()) < rhs; }
999 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1001 future = iter->mFuture;
1002 if(GetFutureState(future) == std::future_status::ready)
1003 mFutureBuffers.erase(iter);
1004 return future;
1007 // Clear out any fulfilled futures.
1008 mFutureBuffers.erase(
1009 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1010 [](const PendingBuffer &entry) -> bool
1011 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1012 ), mFutureBuffers.end()
1016 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1017 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1018 { return hasher(lhs->getName()) < rhs; }
1020 if(iter != mBuffers.end() && (*iter)->getName() == name)
1022 // User asked to create a future buffer that's already loaded. Just
1023 // construct a promise, fulfill the promise immediately, then return a
1024 // shared future that's already set.
1025 Promise<Buffer> promise;
1026 promise.set_value(Buffer(iter->get()));
1027 future = promise.get_future().share();
1028 return future;
1031 Promise<Buffer> promise;
1032 future = promise.get_future().share();
1034 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
1035 Buffer *buffer = std::get_if<Buffer>(&ret);
1036 if(UNLIKELY(!buffer))
1037 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1038 mWakeMutex.lock(); mWakeMutex.unlock();
1039 mWakeThread.notify_all();
1041 mFutureBuffers.insert(
1042 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1043 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1044 { return hasher(lhs.mBuffer->getName()) < rhs; }
1045 ), { buffer->getHandle(), future }
1048 return future;
1051 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1052 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
1054 CheckContext(this);
1056 if(UNLIKELY(!mFutureBuffers.empty()))
1058 // Clear out any fulfilled futures.
1059 mFutureBuffers.erase(
1060 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1061 [](const PendingBuffer &entry) -> bool
1062 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1063 ), mFutureBuffers.end()
1067 auto hasher = std::hash<StringView>();
1068 for(const StringView name : names)
1070 // Check if the buffer that's being created already exists
1071 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1072 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1073 { return hasher(lhs->getName()) < rhs; }
1075 if(iter != mBuffers.end() && (*iter)->getName() == name)
1076 continue;
1078 DecoderOrExceptT dec = findDecoder(name);
1079 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
1080 if(!decoder) continue;
1082 Promise<Buffer> promise;
1083 SharedFuture<Buffer> future = promise.get_future().share();
1085 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
1086 std::move(promise));
1087 Buffer *buffer = std::get_if<Buffer>(&buf);
1088 if(UNLIKELY(!buffer)) continue;
1090 mFutureBuffers.insert(
1091 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1092 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1093 { return hasher(lhs.mBuffer->getName()) < rhs; }
1094 ), { buffer->getHandle(), future }
1097 mWakeMutex.lock(); mWakeMutex.unlock();
1098 mWakeThread.notify_all();
1101 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1102 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder>&& decoder)
1104 CheckContext(this);
1106 auto hasher = std::hash<StringView>();
1107 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1108 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1109 { return hasher(lhs->getName()) < rhs; }
1111 if(iter != mBuffers.end() && (*iter)->getName() == name)
1112 throw std::runtime_error("Buffer already exists");
1114 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1115 Buffer *buffer = std::get_if<Buffer>(&ret);
1116 if(UNLIKELY(!buffer))
1117 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1118 return *buffer;
1121 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1122 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder>&& decoder)
1124 SharedFuture<Buffer> future;
1125 CheckContext(this);
1127 if(UNLIKELY(!mFutureBuffers.empty()))
1129 // Clear out any fulfilled futures.
1130 mFutureBuffers.erase(
1131 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1132 [](const PendingBuffer &entry) -> bool
1133 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1134 ), mFutureBuffers.end()
1138 auto hasher = std::hash<StringView>();
1139 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1140 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1141 { return hasher(lhs->getName()) < rhs; }
1143 if(iter != mBuffers.end() && (*iter)->getName() == name)
1144 throw std::runtime_error("Buffer already exists");
1146 Promise<Buffer> promise;
1147 future = promise.get_future().share();
1149 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1150 Buffer *buffer = std::get_if<Buffer>(&ret);
1151 if(UNLIKELY(!buffer))
1152 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1153 mWakeMutex.lock(); mWakeMutex.unlock();
1154 mWakeThread.notify_all();
1156 mFutureBuffers.insert(
1157 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1158 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1159 { return hasher(lhs.mBuffer->getName()) < rhs; }
1160 ), { buffer->getHandle(), future }
1163 return future;
1167 DECL_THUNK1(Buffer, Context, findBuffer,, StringView)
1168 Buffer ContextImpl::findBuffer(StringView name)
1170 Buffer buffer;
1171 CheckContext(this);
1173 auto hasher = std::hash<StringView>();
1174 if(UNLIKELY(!mFutureBuffers.empty()))
1176 // If the buffer is already pending for the future, wait for it
1177 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1178 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1179 { return hasher(lhs.mBuffer->getName()) < rhs; }
1181 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1183 buffer = iter->mFuture.get();
1184 mFutureBuffers.erase(iter);
1187 // Clear out any completed futures.
1188 mFutureBuffers.erase(
1189 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1190 [](const PendingBuffer &entry) -> bool
1191 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1192 ), mFutureBuffers.end()
1196 if(LIKELY(!buffer))
1198 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1199 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1200 { return hasher(lhs->getName()) < rhs; }
1202 if(iter != mBuffers.end() && (*iter)->getName() == name)
1203 buffer = Buffer(iter->get());
1205 return buffer;
1208 DECL_THUNK1(SharedFuture<Buffer>, Context, findBufferAsync,, StringView)
1209 SharedFuture<Buffer> ContextImpl::findBufferAsync(StringView name)
1211 SharedFuture<Buffer> future;
1212 CheckContext(this);
1214 auto hasher = std::hash<StringView>();
1215 if(UNLIKELY(!mFutureBuffers.empty()))
1217 // Check if the future that's being created already exists
1218 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1219 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1220 { return hasher(lhs.mBuffer->getName()) < rhs; }
1222 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1224 future = iter->mFuture;
1225 if(GetFutureState(future) == std::future_status::ready)
1226 mFutureBuffers.erase(iter);
1227 return future;
1230 // Clear out any fulfilled futures.
1231 mFutureBuffers.erase(
1232 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1233 [](const PendingBuffer &entry) -> bool
1234 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1235 ), mFutureBuffers.end()
1239 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1240 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1241 { return hasher(lhs->getName()) < rhs; }
1243 if(iter != mBuffers.end() && (*iter)->getName() == name)
1245 // User asked to create a future buffer that's already loaded. Just
1246 // construct a promise, fulfill the promise immediately, then return a
1247 // shared future that's already set.
1248 Promise<Buffer> promise;
1249 promise.set_value(Buffer(iter->get()));
1250 future = promise.get_future().share();
1252 return future;
1256 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1257 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1258 void ContextImpl::removeBuffer(StringView name)
1260 CheckContext(this);
1262 auto hasher = std::hash<StringView>();
1263 if(UNLIKELY(!mFutureBuffers.empty()))
1265 // If the buffer is already pending for the future, wait for it to
1266 // finish before continuing.
1267 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1268 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1269 { return hasher(lhs.mBuffer->getName()) < rhs; }
1271 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1273 iter->mFuture.wait();
1274 mFutureBuffers.erase(iter);
1277 // Clear out any completed futures.
1278 mFutureBuffers.erase(
1279 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1280 [](const PendingBuffer &entry) -> bool
1281 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1282 ), mFutureBuffers.end()
1286 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1287 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1288 { return hasher(lhs->getName()) < rhs; }
1290 if(iter != mBuffers.end() && (*iter)->getName() == name)
1292 // Remove pending sources whose future was waiting for this buffer.
1293 mPendingSources.erase(
1294 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1295 [iter](PendingSource &entry) -> bool
1297 return (GetFutureState(entry.mFuture) == std::future_status::ready &&
1298 entry.mFuture.get().getHandle() == iter->get());
1300 ), mPendingSources.end()
1302 (*iter)->cleanup();
1303 mBuffers.erase(iter);
1308 ALuint ContextImpl::getSourceId(ALuint maxprio)
1310 ALuint id = 0;
1311 if(mSourceIds.empty())
1313 alGetError();
1314 alGenSources(1, &id);
1315 if(alGetError() == AL_NO_ERROR)
1316 return id;
1318 SourceImpl *lowest = nullptr;
1319 for(SourceBufferUpdateEntry &entry : mPlaySources)
1321 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1322 lowest = entry.mSource;
1324 for(SourceStreamUpdateEntry &entry : mStreamSources)
1326 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1327 lowest = entry.mSource;
1329 if(lowest && lowest->getPriority() < maxprio)
1331 lowest->stop();
1332 if(mMessage.get())
1333 mMessage->sourceForceStopped(lowest);
1336 if(mSourceIds.empty())
1337 throw std::runtime_error("No available sources");
1339 id = mSourceIds.back();
1340 mSourceIds.pop_back();
1341 return id;
1345 DECL_THUNK0(Source, Context, createSource,)
1346 Source ContextImpl::createSource()
1348 CheckContext(this);
1350 SourceImpl *source;
1351 if(!mFreeSources.empty())
1353 source = mFreeSources.back();
1354 mFreeSources.pop_back();
1356 else
1358 mAllSources.emplace_back(this);
1359 source = &mAllSources.back();
1361 return Source(source);
1365 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1367 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1368 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1369 { return lhs.mSource < rhs; }
1371 if(iter == mPendingSources.end() || iter->mSource != source)
1372 mPendingSources.insert(iter, {source, std::move(future)});
1375 void ContextImpl::removePendingSource(SourceImpl *source)
1377 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1378 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1379 { return lhs.mSource < rhs; }
1381 if(iter != mPendingSources.end() && iter->mSource == source)
1382 mPendingSources.erase(iter);
1385 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1387 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1388 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1389 { return lhs.mSource < rhs; }
1391 return (iter != mPendingSources.end() && iter->mSource == source);
1394 void ContextImpl::addFadingSource(SourceImpl *source)
1396 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1397 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1398 { return lhs < rhs; }
1400 if(iter == mFadingSources.end() || *iter != source)
1401 mFadingSources.insert(iter, source);
1404 void ContextImpl::removeFadingSource(SourceImpl *source)
1406 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1407 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1408 { return lhs < rhs; }
1410 if(iter != mFadingSources.end() && *iter == source)
1411 mFadingSources.erase(iter);
1414 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1416 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1417 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1418 { return lhs.mSource < rhs; }
1420 if(iter == mPlaySources.end() || iter->mSource != source)
1421 mPlaySources.insert(iter, {source,id});
1424 void ContextImpl::addPlayingSource(SourceImpl *source)
1426 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1427 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1428 { return lhs.mSource < rhs; }
1430 if(iter == mStreamSources.end() || iter->mSource != source)
1431 mStreamSources.insert(iter, {source});
1434 void ContextImpl::removePlayingSource(SourceImpl *source)
1436 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1437 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1438 { return lhs.mSource < rhs; }
1440 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1441 mPlaySources.erase(iter0);
1442 else
1444 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1445 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1446 { return lhs.mSource < rhs; }
1448 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1449 mStreamSources.erase(iter1);
1454 void ContextImpl::addStream(SourceImpl *source)
1456 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1457 if(mThread.get_id() == std::thread::id())
1458 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1459 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1460 if(iter == mStreamingSources.end() || *iter != source)
1461 mStreamingSources.insert(iter, source);
1464 void ContextImpl::removeStream(SourceImpl *source)
1466 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1467 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1468 if(iter != mStreamingSources.end() && *iter == source)
1469 mStreamingSources.erase(iter);
1472 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1474 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1475 if(iter != mStreamingSources.end() && *iter == source)
1476 mStreamingSources.erase(iter);
1480 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1481 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1483 if(!hasExtension(AL::EXT_EFX) || !alGenAuxiliaryEffectSlots)
1484 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1485 CheckContext(this);
1487 alGetError();
1488 ALuint id = 0;
1489 alGenAuxiliaryEffectSlots(1, &id);
1490 throw_al_error("Failed to create AuxiliaryEffectSlot");
1491 try {
1492 auto slot = MakeUnique<AuxiliaryEffectSlotImpl>(this, id);
1493 auto iter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), slot);
1494 iter = mEffectSlots.insert(iter, std::move(slot));
1495 return AuxiliaryEffectSlot(iter->get());
1497 catch(...) {
1498 alDeleteAuxiliaryEffectSlots(1, &id);
1499 throw;
1503 void ContextImpl::freeEffectSlot(AuxiliaryEffectSlotImpl *slot)
1505 auto iter = std::lower_bound(mEffectSlots.begin(), mEffectSlots.end(), slot,
1506 [](const UniquePtr<AuxiliaryEffectSlotImpl> &lhs, AuxiliaryEffectSlotImpl *rhs) -> bool
1507 { return lhs.get() < rhs; }
1509 if(iter != mEffectSlots.end() && iter->get() == slot)
1510 mEffectSlots.erase(iter);
1514 DECL_THUNK0(Effect, Context, createEffect,)
1515 Effect ContextImpl::createEffect()
1517 if(!hasExtension(AL::EXT_EFX))
1518 throw std::runtime_error("Effects not supported");
1519 CheckContext(this);
1521 alGetError();
1522 ALuint id = 0;
1523 alGenEffects(1, &id);
1524 throw_al_error("Failed to create Effect");
1525 try {
1526 auto effect = MakeUnique<EffectImpl>(this, id);
1527 auto iter = std::lower_bound(mEffects.begin(), mEffects.end(), effect);
1528 iter = mEffects.insert(iter, std::move(effect));
1529 return Effect(iter->get());
1531 catch(...) {
1532 alDeleteEffects(1, &id);
1533 throw;
1537 void ContextImpl::freeEffect(EffectImpl *effect)
1539 auto iter = std::lower_bound(mEffects.begin(), mEffects.end(), effect,
1540 [](const UniquePtr<EffectImpl> &lhs, EffectImpl *rhs) -> bool
1541 { return lhs.get() < rhs; }
1543 if(iter != mEffects.end() && iter->get() == effect)
1544 mEffects.erase(iter);
1548 DECL_THUNK0(SourceGroup, Context, createSourceGroup,)
1549 SourceGroup ContextImpl::createSourceGroup()
1551 auto srcgroup = MakeUnique<SourceGroupImpl>(this);
1552 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), srcgroup);
1554 iter = mSourceGroups.insert(iter, std::move(srcgroup));
1555 return SourceGroup(iter->get());
1558 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1560 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group,
1561 [](const UniquePtr<SourceGroupImpl> &lhs, SourceGroupImpl *rhs) -> bool
1562 { return lhs.get() < rhs; }
1564 if(iter != mSourceGroups.end() && iter->get() == group)
1565 mSourceGroups.erase(iter);
1569 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1570 void ContextImpl::setDopplerFactor(ALfloat factor)
1572 if(!(factor >= 0.0f))
1573 throw std::out_of_range("Doppler factor out of range");
1574 CheckContext(this);
1575 alDopplerFactor(factor);
1579 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1580 void ContextImpl::setSpeedOfSound(ALfloat speed)
1582 if(!(speed > 0.0f))
1583 throw std::out_of_range("Speed of sound out of range");
1584 CheckContext(this);
1585 alSpeedOfSound(speed);
1589 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1590 void ContextImpl::setDistanceModel(DistanceModel model)
1592 CheckContext(this);
1593 alDistanceModel((ALenum)model);
1597 DECL_THUNK0(void, Context, update,)
1598 void ContextImpl::update()
1600 CheckContext(this);
1601 mPendingSources.erase(
1602 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1603 [](PendingSource &entry) -> bool
1604 { return !entry.mSource->checkPending(entry.mFuture); }
1605 ), mPendingSources.end()
1607 if(!mFadingSources.empty())
1609 auto cur_time = std::chrono::steady_clock::now().time_since_epoch();
1610 mFadingSources.erase(
1611 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1612 [cur_time](SourceImpl *source) -> bool
1613 { return !source->fadeUpdate(cur_time); }
1614 ), mFadingSources.end()
1617 mPlaySources.erase(
1618 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1619 [](const SourceBufferUpdateEntry &entry) -> bool
1620 { return !entry.mSource->playUpdate(entry.mId); }
1621 ), mPlaySources.end()
1623 mStreamSources.erase(
1624 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1625 [](const SourceStreamUpdateEntry &entry) -> bool
1626 { return !entry.mSource->playUpdate(); }
1627 ), mStreamSources.end()
1630 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1632 // For performance reasons, don't wait for the thread's mutex. This
1633 // should be called often enough to keep up with any and all streams
1634 // regardless.
1635 mWakeThread.notify_all();
1638 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1640 ALCint connected;
1641 alcGetIntegerv(mDevice->getALCdevice(), ALC_CONNECTED, 1, &connected);
1642 mIsConnected = connected;
1643 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1647 DECL_THUNK0(Device, Context, getDevice,)
1648 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1649 DECL_THUNK0(Listener, Context, getListener,)
1650 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1652 void Context::MakeCurrent(Context context)
1653 { ContextImpl::MakeCurrent(context.pImpl); }
1655 Context Context::GetCurrent()
1656 { return Context(ContextImpl::GetCurrent()); }
1658 void Context::MakeThreadCurrent(Context context)
1659 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1661 Context Context::GetThreadCurrent()
1662 { return Context(ContextImpl::GetThreadCurrent()); }
1665 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1666 void ListenerImpl::setGain(ALfloat gain)
1668 if(!(gain >= 0.0f))
1669 throw std::out_of_range("Gain out of range");
1670 CheckContext(mContext);
1671 alListenerf(AL_GAIN, gain);
1675 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1676 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1678 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1679 CheckContext(mContext);
1680 Batcher batcher = mContext->getBatcher();
1681 alListenerfv(AL_POSITION, position.getPtr());
1682 alListenerfv(AL_VELOCITY, velocity.getPtr());
1683 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1686 DECL_THUNK1(void, Listener, setPosition,, const Vector3&)
1687 void ListenerImpl::setPosition(const Vector3 &position)
1689 CheckContext(mContext);
1690 alListenerfv(AL_POSITION, position.getPtr());
1693 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1694 void ListenerImpl::setPosition(const ALfloat *pos)
1696 CheckContext(mContext);
1697 alListenerfv(AL_POSITION, pos);
1700 DECL_THUNK1(void, Listener, setVelocity,, const Vector3&)
1701 void ListenerImpl::setVelocity(const Vector3 &velocity)
1703 CheckContext(mContext);
1704 alListenerfv(AL_VELOCITY, velocity.getPtr());
1707 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1708 void ListenerImpl::setVelocity(const ALfloat *vel)
1710 CheckContext(mContext);
1711 alListenerfv(AL_VELOCITY, vel);
1714 DECL_THUNK1(void, Listener, setOrientation,, const Vector3Pair&)
1715 void ListenerImpl::setOrientation(const std::pair<Vector3,Vector3> &orientation)
1717 CheckContext(mContext);
1718 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1721 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1722 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1724 CheckContext(mContext);
1725 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1726 alListenerfv(AL_ORIENTATION, ori);
1729 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1730 void ListenerImpl::setOrientation(const ALfloat *ori)
1732 CheckContext(mContext);
1733 alListenerfv(AL_ORIENTATION, ori);
1736 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)
1737 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1739 if(!(m_u > 0.0f))
1740 throw std::out_of_range("Invalid meters per unit");
1741 CheckContext(mContext);
1742 if(mContext->hasExtension(AL::EXT_EFX))
1743 alListenerf(AL_METERS_PER_UNIT, m_u);