Rename parse_timeval for consistency
[alure.git] / src / context.cpp
blob9d067ceb8e91fd69e355d63053515fa9cfba0a5e
2 #include "config.h"
4 #include "context.h"
6 #include <stdexcept>
7 #include <algorithm>
8 #include <functional>
9 #include <memory>
10 #include <iostream>
11 #include <sstream>
12 #include <fstream>
13 #include <cstring>
14 #include <map>
15 #include <new>
17 #include "alc.h"
19 #ifdef HAVE_VORBISFILE
20 #include "decoders/vorbisfile.hpp"
21 #endif
22 #ifdef HAVE_LIBFLAC
23 #include "decoders/flac.hpp"
24 #endif
25 #ifdef HAVE_OPUSFILE
26 #include "decoders/opusfile.hpp"
27 #endif
28 #ifdef HAVE_LIBSNDFILE
29 #include "decoders/sndfile.hpp"
30 #endif
31 #ifdef HAVE_MPG123
32 #include "decoders/mpg123.hpp"
33 #endif
34 #include "decoders/wave.hpp"
36 #include "devicemanager.h"
37 #include "device.h"
38 #include "buffer.h"
39 #include "source.h"
40 #include "auxeffectslot.h"
41 #include "effect.h"
42 #include "sourcegroup.h"
44 #ifdef _WIN32
45 #define WIN32_LEAN_AND_MEAN
46 #include <windows.h>
47 #endif
49 namespace std
52 // Implements a FNV-1a hash for StringView. NOTE: This is *NOT* guaranteed
53 // compatible with std::hash<String>! The standard does not give any specific
54 // hash implementation, nor a way for applications to access the same hash
55 // function as std::string (short of copying into a string and hashing that).
56 // So if you need Strings and StringViews to result in the same hash for the
57 // same set of characters, hash StringViews created from the Strings.
58 template<>
59 struct hash<alure::StringView> {
60 size_t operator()(const alure::StringView &str) const noexcept
62 using traits_type = alure::StringView::traits_type;
64 if /*constexpr*/ (sizeof(size_t) == 8)
66 static constexpr size_t hash_offset = 0xcbf29ce484222325;
67 static constexpr size_t hash_prime = 0x100000001b3;
69 size_t val = hash_offset;
70 for(auto ch : str)
71 val = (val^traits_type::to_int_type(ch)) * hash_prime;
72 return val;
74 else
76 static constexpr size_t hash_offset = 0x811c9dc5;
77 static constexpr size_t hash_prime = 0x1000193;
79 size_t val = hash_offset;
80 for(auto ch : str)
81 val = (val^traits_type::to_int_type(ch)) * hash_prime;
82 return val;
89 namespace
92 // Global mutex to protect global context changes
93 std::mutex mGlobalCtxMutex;
95 #ifdef _WIN32
96 // Windows' std::ifstream fails with non-ANSI paths since the standard only
97 // specifies names using const char* (or std::string). MSVC has a non-standard
98 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
99 // but not all Windows compilers support it. So we have to make our own istream
100 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
101 class StreamBuf final : public std::streambuf {
102 alure::Array<char_type,4096> mBuffer;
103 HANDLE mFile;
105 int_type underflow() override
107 if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
109 // Read in the next chunk of data, and set the pointers on success
110 DWORD got = 0;
111 if(!ReadFile(mFile, mBuffer.data(), mBuffer.size(), &got, NULL))
112 got = 0;
113 setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
115 if(gptr() == egptr())
116 return traits_type::eof();
117 return traits_type::to_int_type(*gptr());
120 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
122 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
123 return traits_type::eof();
125 LARGE_INTEGER fpos;
126 switch(whence)
128 case std::ios_base::beg:
129 fpos.QuadPart = offset;
130 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
131 return traits_type::eof();
132 break;
134 case std::ios_base::cur:
135 // If the offset remains in the current buffer range, just
136 // update the pointer.
137 if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
138 (offset < 0 && -offset <= off_type(gptr()-eback())))
140 // Get the current file offset to report the correct read
141 // offset.
142 fpos.QuadPart = 0;
143 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
144 return traits_type::eof();
145 setg(eback(), gptr()+offset, egptr());
146 return fpos.QuadPart - off_type(egptr()-gptr());
148 // Need to offset for the file offset being at egptr() while
149 // the requested offset is relative to gptr().
150 offset -= off_type(egptr()-gptr());
151 fpos.QuadPart = offset;
152 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
153 return traits_type::eof();
154 break;
156 case std::ios_base::end:
157 fpos.QuadPart = offset;
158 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
159 return traits_type::eof();
160 break;
162 default:
163 return traits_type::eof();
165 setg(0, 0, 0);
166 return fpos.QuadPart;
169 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
171 // Simplified version of seekoff
172 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
173 return traits_type::eof();
175 LARGE_INTEGER fpos;
176 fpos.QuadPart = pos;
177 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
178 return traits_type::eof();
180 setg(0, 0, 0);
181 return fpos.QuadPart;
184 public:
185 bool open(const char *filename)
187 alure::Vector<wchar_t> wname;
188 int wnamelen;
190 wnamelen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
191 if(wnamelen <= 0) return false;
193 wname.resize(wnamelen);
194 MultiByteToWideChar(CP_UTF8, 0, filename, -1, wname.data(), wnamelen);
196 mFile = CreateFileW(wname.data(), GENERIC_READ, FILE_SHARE_READ, NULL,
197 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
198 if(mFile == INVALID_HANDLE_VALUE) return false;
199 return true;
202 bool is_open() const { return mFile != INVALID_HANDLE_VALUE; }
204 StreamBuf() : mFile(INVALID_HANDLE_VALUE)
206 ~StreamBuf() override
208 if(mFile != INVALID_HANDLE_VALUE)
209 CloseHandle(mFile);
210 mFile = INVALID_HANDLE_VALUE;
214 // Inherit from std::istream to use our custom streambuf
215 class Stream final : public std::istream {
216 public:
217 Stream(const char *filename) : std::istream(new StreamBuf())
219 // Set the failbit if the file failed to open.
220 if(!(static_cast<StreamBuf*>(rdbuf())->open(filename)))
221 clear(failbit);
223 ~Stream() override
224 { delete rdbuf(); }
226 bool is_open() const { return static_cast<StreamBuf*>(rdbuf())->is_open(); }
228 #endif
230 using DecoderEntryPair = std::pair<alure::String,alure::UniquePtr<alure::DecoderFactory>>;
231 const DecoderEntryPair sDefaultDecoders[] = {
232 { "_alure_int_wave", alure::MakeUnique<alure::WaveDecoderFactory>() },
234 #ifdef HAVE_VORBISFILE
235 { "_alure_int_vorbis", alure::MakeUnique<alure::VorbisFileDecoderFactory>() },
236 #endif
237 #ifdef HAVE_LIBFLAC
238 { "_alure_int_flac", alure::MakeUnique<alure::FlacDecoderFactory>() },
239 #endif
240 #ifdef HAVE_OPUSFILE
241 { "_alure_int_opus", alure::MakeUnique<alure::OpusFileDecoderFactory>() },
242 #endif
243 #ifdef HAVE_LIBSNDFILE
244 { "_alure_int_sndfile", alure::MakeUnique<alure::SndFileDecoderFactory>() },
245 #endif
246 #ifdef HAVE_MPG123
247 { "_alure_int_mpg123", alure::MakeUnique<alure::Mpg123DecoderFactory>() },
248 #endif
250 alure::Vector<DecoderEntryPair> sDecoders;
253 template<typename T>
254 alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> &file, T start, T end)
256 alure::DecoderOrExceptT ret;
257 while(start != end)
259 alure::DecoderFactory *factory = start->second.get();
260 auto decoder = factory->createDecoder(file);
261 if(decoder) return (ret = std::move(decoder));
263 if(!file || !(file->clear(),file->seekg(0)))
264 return (ret = std::make_exception_ptr(std::runtime_error(
265 "Failed to rewind file for the next decoder factory"
266 )));
268 ++start;
271 return (ret = alure::SharedPtr<alure::Decoder>(nullptr));
274 static alure::DecoderOrExceptT GetDecoder(alure::UniquePtr<std::istream> file)
276 auto decoder = GetDecoder(file, sDecoders.begin(), sDecoders.end());
277 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
278 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
279 decoder = GetDecoder(file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
280 if(std::holds_alternative<std::exception_ptr>(decoder)) return decoder;
281 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
282 return (decoder = std::make_exception_ptr(std::runtime_error("No decoder found")));
285 class DefaultFileIOFactory final : public alure::FileIOFactory {
286 alure::UniquePtr<std::istream> openFile(const alure::String &name) noexcept override
288 #ifdef _WIN32
289 auto file = alure::MakeUnique<Stream>(name.c_str());
290 #else
291 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
292 #endif
293 if(!file->is_open()) file = nullptr;
294 return std::move(file);
297 DefaultFileIOFactory sDefaultFileFactory;
299 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
303 namespace alure
306 std::variant<std::monostate,uint64_t> ParseTimeval(StringView strval, double srate)
308 size_t cpos = strval.find_first_of(':');
309 if(cpos == StringView::npos)
311 // No colon is present, treat it as a plain sample offset
312 char *end;
313 auto str = String(strval);
314 uint64_t val = std::strtoull(str.c_str(), &end, 10);
315 if(*end != 0) return {};
316 return val;
319 // Value is not a sample offset. Its format is [[HH:]MM]:SS[.sss] (at
320 // least one colon must exist to be interpreted this way).
321 uint64_t val = 0;
322 String str;
323 char *end;
325 if(cpos != 0)
327 // If a non-empty first value, parse it (may be hours or minutes)
328 str = String(strval.data(), cpos);
329 val = std::strtoul(str.c_str(), &end, 10);
330 if(*end != 0) return {};
333 strval = strval.substr(cpos+1);
334 cpos = strval.find_first_of(':');
335 if(cpos != StringView::npos)
337 // If a second colon is present, the first value was hours and this is
338 // minutes, otherwise the first value was minutes.
339 str = String(strval.data(), cpos);
340 uint64_t val2 = std::strtoul(str.c_str(), &end, 10);
341 if(*end != 0 || 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 if(dpos == StringView::npos)
357 str = String(strval);
358 else
359 str = String(strval.substr(0, dpos+4));
360 secs = std::strtod(str.c_str(), &end);
361 if(*end != 0 || !(secs >= 0.0 && secs < 60.0))
362 return {};
365 // Convert minutes to seconds, add the seconds, then convert to samples.
366 return static_cast<uint64_t>((val*60.0 + secs) * srate);
370 Decoder::~Decoder() { }
371 DecoderFactory::~DecoderFactory() { }
373 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
375 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
376 [](const DecoderEntryPair &entry, StringView rhs) -> bool
377 { return entry.first < rhs; }
379 if(iter != sDecoders.end())
380 throw std::runtime_error("Decoder factory already registered");
381 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
384 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name) noexcept
386 UniquePtr<DecoderFactory> factory;
387 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
388 [](const DecoderEntryPair &entry, StringView rhs) noexcept -> bool
389 { return entry.first < rhs; }
391 if(iter != sDecoders.end())
393 factory = std::move(iter->second);
394 sDecoders.erase(iter);
395 return factory;
397 return factory;
401 FileIOFactory::~FileIOFactory() { }
403 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory) noexcept
405 sFileFactory.swap(factory);
406 return factory;
409 FileIOFactory &FileIOFactory::get() noexcept
411 FileIOFactory *factory = sFileFactory.get();
412 if(factory) return *factory;
413 return sDefaultFileFactory;
417 // Default message handler methods are no-ops.
418 MessageHandler::~MessageHandler()
422 void MessageHandler::deviceDisconnected(Device) noexcept
426 void MessageHandler::sourceStopped(Source) noexcept
430 void MessageHandler::sourceForceStopped(Source) noexcept
434 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>) noexcept
438 String MessageHandler::resourceNotFound(StringView) noexcept
440 return String();
444 template<typename T>
445 static inline void LoadALFunc(T **func, const char *name)
446 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
448 static void LoadNothing(ContextImpl*) { }
450 static void LoadEFX(ContextImpl *ctx)
452 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
453 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
454 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
455 LoadALFunc(&ctx->alEffecti, "alEffecti");
456 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
457 LoadALFunc(&ctx->alEffectf, "alEffectf");
458 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
459 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
460 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
461 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
462 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
464 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
465 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
466 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
467 LoadALFunc(&ctx->alFilteri, "alFilteri");
468 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
469 LoadALFunc(&ctx->alFilterf, "alFilterf");
470 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
471 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
472 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
473 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
474 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
476 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
477 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
478 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
479 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
480 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
481 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
482 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
483 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
484 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
485 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
486 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
489 static void LoadSourceResampler(ContextImpl *ctx)
491 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
494 static void LoadSourceLatency(ContextImpl *ctx)
496 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
497 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
500 static const struct {
501 enum AL extension;
502 const char name[32];
503 void (&loader)(ContextImpl*);
504 } ALExtensionList[] = {
505 { AL::EXT_EFX, "ALC_EXT_EFX", LoadEFX },
507 { AL::EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
508 { AL::EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
509 { AL::EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
511 { AL::EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
512 { AL::EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
513 { AL::EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
515 { AL::SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
516 { AL::SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
517 { AL::SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
518 { AL::SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
520 { AL::EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
522 { AL::EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
523 { AL::EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
527 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
528 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
530 std::atomic<uint64_t> ContextImpl::sContextSetCount{0};
532 void ContextImpl::MakeCurrent(ContextImpl *context)
534 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
536 if(alcMakeContextCurrent(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
537 throw std::runtime_error("Call to alcMakeContextCurrent failed");
538 if(context)
540 context->addRef();
541 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
543 std::swap(sCurrentCtx, context);
544 if(context) context->decRef();
546 if(sThreadCurrentCtx)
547 sThreadCurrentCtx->decRef();
548 sThreadCurrentCtx = nullptr;
549 sContextSetCount.fetch_add(1, std::memory_order_release);
551 if((context = sCurrentCtx) != nullptr)
553 ctxlock.unlock();
554 context->mWakeThread.notify_all();
558 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
560 if(!DeviceManagerImpl::SetThreadContext)
561 throw std::runtime_error("Thread-local contexts unsupported");
562 if(DeviceManagerImpl::SetThreadContext(context ? context->getALCcontext() : nullptr) == ALC_FALSE)
563 throw std::runtime_error("Call to alcSetThreadContext failed");
564 if(context)
566 context->addRef();
567 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
569 if(sThreadCurrentCtx)
570 sThreadCurrentCtx->decRef();
571 sThreadCurrentCtx = context;
572 sContextSetCount.fetch_add(1, std::memory_order_release);
575 void ContextImpl::setupExts()
577 ALCdevice *device = mDevice->getALCdevice();
578 mHasExt.clear();
579 for(const auto &entry : ALExtensionList)
581 if((strncmp(entry.name, "ALC", 3) == 0) ? alcIsExtensionPresent(device, entry.name) :
582 alIsExtensionPresent(entry.name))
584 mHasExt.set(static_cast<size_t>(entry.extension));
585 entry.loader(this);
591 void ContextImpl::backgroundProc()
593 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(ALC::EXT_thread_local_context))
594 DeviceManagerImpl::SetThreadContext(getALCcontext());
596 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
597 std::chrono::milliseconds waketime(0);
598 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
599 while(!mQuitThread.load(std::memory_order_acquire))
602 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
603 mStreamingSources.erase(
604 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
605 [](SourceImpl *source) -> bool
606 { return !source->updateAsync(); }
607 ), mStreamingSources.end()
611 // Only do one pending buffer at a time. In case there's several large
612 // buffers to load, we still need to process streaming sources so they
613 // don't underrun.
614 PendingPromise *lastpb = mPendingCurrent.load(std::memory_order_acquire);
615 if(PendingPromise *pb = lastpb->mNext.load(std::memory_order_relaxed))
617 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
618 pb->mPromise.set_value(Buffer(pb->mBuffer));
619 Promise<Buffer>().swap(pb->mPromise);
620 mPendingCurrent.store(pb, std::memory_order_release);
621 continue;
624 std::unique_lock<std::mutex> wakelock(mWakeMutex);
625 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
627 ctxlock.unlock();
629 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
630 if(interval.count() == 0)
631 mWakeThread.wait(wakelock);
632 else
634 auto now = std::chrono::steady_clock::now() - basetime;
635 if(now > waketime)
637 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
638 waketime += interval * mult;
640 mWakeThread.wait_until(wakelock, waketime + basetime);
642 wakelock.unlock();
644 ctxlock.lock();
645 while(!mQuitThread.load(std::memory_order_acquire) &&
646 alcGetCurrentContext() != getALCcontext())
647 mWakeThread.wait(ctxlock);
650 ctxlock.unlock();
652 if(DeviceManagerImpl::SetThreadContext)
653 DeviceManagerImpl::SetThreadContext(nullptr);
657 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
658 : mContextSetCounter(std::numeric_limits<uint64_t>::max()),
659 mListener(this), mContext(context), mDevice(device),
660 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
661 mRefs(0), mIsConnected(true), mIsBatching(false),
662 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
663 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
664 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
665 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
666 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
667 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
668 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
669 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
670 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
671 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
673 mHasExt.clear();
674 mSourceIds.reserve(256);
675 mPendingHead = new PendingPromise;
676 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
677 mPendingTail = mPendingHead;
680 ContextImpl::~ContextImpl()
682 PendingPromise *pb = mPendingTail;
683 while(pb)
685 PendingPromise *next = pb->mNext.load(std::memory_order_relaxed);
686 delete pb;
687 pb = next;
689 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
690 mPendingHead = nullptr;
691 mPendingTail = nullptr;
695 void Context::destroy()
697 ContextImpl *i = pImpl;
698 pImpl = nullptr;
699 i->destroy();
701 void ContextImpl::destroy()
703 if(mRefs != 0)
704 throw std::runtime_error("Context is in use");
705 if(!mBuffers.empty())
706 throw std::runtime_error("Trying to destroy a context with buffers");
708 if(mThread.joinable())
710 std::unique_lock<std::mutex> lock(mWakeMutex);
711 mQuitThread.store(true, std::memory_order_release);
712 lock.unlock();
713 mWakeThread.notify_all();
714 mThread.join();
717 alcDestroyContext(mContext);
718 mContext = nullptr;
720 mDevice->removeContext(this);
724 DECL_THUNK0(void, Context, startBatch,)
725 void ContextImpl::startBatch()
727 alcSuspendContext(mContext);
728 mIsBatching = true;
731 DECL_THUNK0(void, Context, endBatch,)
732 void ContextImpl::endBatch()
734 alcProcessContext(mContext);
735 mIsBatching = false;
739 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
740 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler>&& handler)
742 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
743 mMessage.swap(handler);
744 return handler;
748 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
749 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
751 if(interval.count() < 0 || interval > std::chrono::seconds(1))
752 throw std::out_of_range("Async wake interval out of range");
753 mWakeInterval.store(interval);
754 mWakeMutex.lock(); mWakeMutex.unlock();
755 mWakeThread.notify_all();
759 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
761 DecoderOrExceptT ret;
763 String oldname = String(name);
764 auto file = FileIOFactory::get().openFile(oldname);
765 if(file) return (ret = GetDecoder(std::move(file)));
767 // Resource not found. Try to find a substitute.
768 if(!mMessage.get())
769 return (ret = std::make_exception_ptr(std::runtime_error("Failed to open file")));
770 do {
771 String newname(mMessage->resourceNotFound(oldname));
772 if(newname.empty())
773 return (ret = std::make_exception_ptr(std::runtime_error("Failed to open file")));
774 file = FileIOFactory::get().openFile(newname);
775 oldname = std::move(newname);
776 } while(!file);
778 return (ret = GetDecoder(std::move(file)));
781 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
782 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
784 CheckContext(this);
785 DecoderOrExceptT dec = findDecoder(name);
786 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
787 return *decoder;
788 std::rethrow_exception(std::get<std::exception_ptr>(dec));
792 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
793 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
795 CheckContext(this);
796 return GetFormat(channels, type) != AL_NONE;
800 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
801 ArrayView<String> ContextImpl::getAvailableResamplers()
803 CheckContext(this);
804 if(mResamplers.empty() && hasExtension(AL::SOFT_source_resampler))
806 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
807 mResamplers.reserve(num_resamplers);
808 for(int i = 0;i < num_resamplers;i++)
809 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
810 if(mResamplers.empty())
811 mResamplers.emplace_back();
813 return mResamplers;
816 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
817 ALsizei ContextImpl::getDefaultResamplerIndex() const
819 CheckContext(this);
820 if(!hasExtension(AL::SOFT_source_resampler))
821 return 0;
822 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
826 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
828 BufferOrExceptT retval;
829 ALuint srate = decoder->getFrequency();
830 ChannelConfig chans = decoder->getChannelConfig();
831 SampleType type = decoder->getSampleType();
832 ALuint frames = decoder->getLength();
834 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
835 frames = decoder->read(data.data(), frames);
836 if(!frames)
837 return (retval = std::make_exception_ptr(std::runtime_error("No samples for buffer")));
838 data.resize(FramesToBytes(frames, chans, type));
840 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
841 if(loop_pts.first >= loop_pts.second)
842 loop_pts = std::make_pair(0, frames);
843 else
845 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
846 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
849 // Get the format before calling the bufferLoading message handler, to
850 // ensure it's something OpenAL can handle.
851 ALenum format = GetFormat(chans, type);
852 if(format == AL_NONE)
854 String str("Unsupported format (");
855 str += GetSampleTypeName(type);
856 str += ", ";
857 str += GetChannelConfigName(chans);
858 str += ")";
859 return (retval = std::make_exception_ptr(std::runtime_error(str)));
862 if(mMessage.get())
863 mMessage->bufferLoading(name, chans, type, srate, data);
865 alGetError();
866 ALuint bid = 0;
867 alGenBuffers(1, &bid);
868 alBufferData(bid, format, data.data(), data.size(), srate);
869 if(hasExtension(AL::SOFT_loop_points))
871 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
872 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
874 ALenum err = alGetError();
875 if(err != AL_NO_ERROR)
877 alDeleteBuffers(1, &bid);
878 return (retval = std::make_exception_ptr(al_error(err, "Failed to buffer data")));
881 return (retval = mBuffers.insert(iter,
882 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
883 )->get());
886 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
888 BufferOrExceptT retval;
889 ALuint srate = decoder->getFrequency();
890 ChannelConfig chans = decoder->getChannelConfig();
891 SampleType type = decoder->getSampleType();
892 ALuint frames = decoder->getLength();
893 if(!frames)
894 return (retval = std::make_exception_ptr(std::runtime_error("No samples for buffer")));
896 ALenum format = GetFormat(chans, type);
897 if(format == AL_NONE)
899 std::stringstream sstr;
900 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
901 return (retval = std::make_exception_ptr(std::runtime_error(sstr.str())));
904 alGetError();
905 ALuint bid = 0;
906 alGenBuffers(1, &bid);
907 ALenum err = alGetError();
908 if(err != AL_NO_ERROR)
909 return (retval = std::make_exception_ptr(al_error(err, "Failed to create buffer")));
911 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
913 if(mThread.get_id() == std::thread::id())
914 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
916 PendingPromise *pf = nullptr;
917 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
918 pf = new PendingPromise{buffer.get(), decoder, format, frames, std::move(promise)};
919 else
921 pf = mPendingTail;
922 pf->mBuffer = buffer.get();
923 pf->mDecoder = decoder;
924 pf->mFormat = format;
925 pf->mFrames = frames;
926 pf->mPromise = std::move(promise);
927 mPendingTail = pf->mNext.exchange(nullptr, std::memory_order_relaxed);
930 mPendingHead->mNext.store(pf, std::memory_order_release);
931 mPendingHead = pf;
933 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
936 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
937 Buffer ContextImpl::getBuffer(StringView name)
939 CheckContext(this);
941 auto hasher = std::hash<StringView>();
942 if(UNLIKELY(!mFutureBuffers.empty()))
944 Buffer buffer;
946 // If the buffer is already pending for the future, wait for it
947 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
948 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
949 { return hasher(lhs.mBuffer->getName()) < rhs; }
951 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
953 buffer = iter->mFuture.get();
954 mFutureBuffers.erase(iter);
957 // Clear out any completed futures.
958 mFutureBuffers.erase(
959 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
960 [](const PendingBuffer &entry) -> bool
961 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
962 ), mFutureBuffers.end()
965 // If we got the buffer, return it. Otherwise, go load it normally.
966 if(buffer) return buffer;
969 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
970 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
971 { return hasher(lhs->getName()) < rhs; }
973 if(iter != mBuffers.end() && (*iter)->getName() == name)
974 return Buffer(iter->get());
976 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
977 Buffer *buffer = std::get_if<Buffer>(&ret);
978 if(UNLIKELY(!buffer))
979 std::rethrow_exception(std::get<std::exception_ptr>(ret));
980 return *buffer;
983 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
984 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
986 SharedFuture<Buffer> future;
987 CheckContext(this);
989 auto hasher = std::hash<StringView>();
990 if(UNLIKELY(!mFutureBuffers.empty()))
992 // Check if the future that's being created already exists
993 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
994 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
995 { return hasher(lhs.mBuffer->getName()) < rhs; }
997 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
999 future = iter->mFuture;
1000 if(GetFutureState(future) == std::future_status::ready)
1001 mFutureBuffers.erase(iter);
1002 return future;
1005 // Clear out any fulfilled futures.
1006 mFutureBuffers.erase(
1007 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1008 [](const PendingBuffer &entry) -> bool
1009 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1010 ), mFutureBuffers.end()
1014 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1015 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1016 { return hasher(lhs->getName()) < rhs; }
1018 if(iter != mBuffers.end() && (*iter)->getName() == name)
1020 // User asked to create a future buffer that's already loaded. Just
1021 // construct a promise, fulfill the promise immediately, then return a
1022 // shared future that's already set.
1023 Promise<Buffer> promise;
1024 promise.set_value(Buffer(iter->get()));
1025 future = promise.get_future().share();
1026 return future;
1029 Promise<Buffer> promise;
1030 future = promise.get_future().share();
1032 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
1033 Buffer *buffer = std::get_if<Buffer>(&ret);
1034 if(UNLIKELY(!buffer))
1035 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1036 mWakeMutex.lock(); mWakeMutex.unlock();
1037 mWakeThread.notify_all();
1039 mFutureBuffers.insert(
1040 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1041 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1042 { return hasher(lhs.mBuffer->getName()) < rhs; }
1043 ), { buffer->getHandle(), future }
1046 return future;
1049 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1050 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
1052 CheckContext(this);
1054 if(UNLIKELY(!mFutureBuffers.empty()))
1056 // Clear out any fulfilled futures.
1057 mFutureBuffers.erase(
1058 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1059 [](const PendingBuffer &entry) -> bool
1060 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1061 ), mFutureBuffers.end()
1065 auto hasher = std::hash<StringView>();
1066 for(const StringView name : names)
1068 // Check if the buffer that's being created already exists
1069 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1070 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1071 { return hasher(lhs->getName()) < rhs; }
1073 if(iter != mBuffers.end() && (*iter)->getName() == name)
1074 continue;
1076 DecoderOrExceptT dec = findDecoder(name);
1077 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
1078 if(!decoder) continue;
1080 Promise<Buffer> promise;
1081 SharedFuture<Buffer> future = promise.get_future().share();
1083 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
1084 std::move(promise));
1085 Buffer *buffer = std::get_if<Buffer>(&buf);
1086 if(UNLIKELY(!buffer)) continue;
1088 mFutureBuffers.insert(
1089 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1090 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1091 { return hasher(lhs.mBuffer->getName()) < rhs; }
1092 ), { buffer->getHandle(), future }
1095 mWakeMutex.lock(); mWakeMutex.unlock();
1096 mWakeThread.notify_all();
1099 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1100 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder>&& decoder)
1102 CheckContext(this);
1104 auto hasher = std::hash<StringView>();
1105 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1106 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1107 { return hasher(lhs->getName()) < rhs; }
1109 if(iter != mBuffers.end() && (*iter)->getName() == name)
1110 throw std::runtime_error("Buffer already exists");
1112 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1113 Buffer *buffer = std::get_if<Buffer>(&ret);
1114 if(UNLIKELY(!buffer))
1115 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1116 return *buffer;
1119 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1120 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder>&& decoder)
1122 SharedFuture<Buffer> future;
1123 CheckContext(this);
1125 if(UNLIKELY(!mFutureBuffers.empty()))
1127 // Clear out any fulfilled futures.
1128 mFutureBuffers.erase(
1129 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1130 [](const PendingBuffer &entry) -> bool
1131 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1132 ), mFutureBuffers.end()
1136 auto hasher = std::hash<StringView>();
1137 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1138 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1139 { return hasher(lhs->getName()) < rhs; }
1141 if(iter != mBuffers.end() && (*iter)->getName() == name)
1142 throw std::runtime_error("Buffer already exists");
1144 Promise<Buffer> promise;
1145 future = promise.get_future().share();
1147 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1148 Buffer *buffer = std::get_if<Buffer>(&ret);
1149 if(UNLIKELY(!buffer))
1150 std::rethrow_exception(std::get<std::exception_ptr>(ret));
1151 mWakeMutex.lock(); mWakeMutex.unlock();
1152 mWakeThread.notify_all();
1154 mFutureBuffers.insert(
1155 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1156 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1157 { return hasher(lhs.mBuffer->getName()) < rhs; }
1158 ), { buffer->getHandle(), future }
1161 return future;
1165 DECL_THUNK1(Buffer, Context, findBuffer,, StringView)
1166 Buffer ContextImpl::findBuffer(StringView name)
1168 Buffer buffer;
1169 CheckContext(this);
1171 auto hasher = std::hash<StringView>();
1172 if(UNLIKELY(!mFutureBuffers.empty()))
1174 // If the buffer is already pending for the future, wait for it
1175 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1176 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1177 { return hasher(lhs.mBuffer->getName()) < rhs; }
1179 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1181 buffer = iter->mFuture.get();
1182 mFutureBuffers.erase(iter);
1185 // Clear out any completed futures.
1186 mFutureBuffers.erase(
1187 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1188 [](const PendingBuffer &entry) -> bool
1189 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1190 ), mFutureBuffers.end()
1194 if(LIKELY(!buffer))
1196 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1197 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1198 { return hasher(lhs->getName()) < rhs; }
1200 if(iter != mBuffers.end() && (*iter)->getName() == name)
1201 buffer = Buffer(iter->get());
1203 return buffer;
1206 DECL_THUNK1(SharedFuture<Buffer>, Context, findBufferAsync,, StringView)
1207 SharedFuture<Buffer> ContextImpl::findBufferAsync(StringView name)
1209 SharedFuture<Buffer> future;
1210 CheckContext(this);
1212 auto hasher = std::hash<StringView>();
1213 if(UNLIKELY(!mFutureBuffers.empty()))
1215 // Check if the future that's being created already exists
1216 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1217 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1218 { return hasher(lhs.mBuffer->getName()) < rhs; }
1220 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1222 future = iter->mFuture;
1223 if(GetFutureState(future) == std::future_status::ready)
1224 mFutureBuffers.erase(iter);
1225 return future;
1228 // Clear out any fulfilled futures.
1229 mFutureBuffers.erase(
1230 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1231 [](const PendingBuffer &entry) -> bool
1232 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1233 ), mFutureBuffers.end()
1237 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1238 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1239 { return hasher(lhs->getName()) < rhs; }
1241 if(iter != mBuffers.end() && (*iter)->getName() == name)
1243 // User asked to create a future buffer that's already loaded. Just
1244 // construct a promise, fulfill the promise immediately, then return a
1245 // shared future that's already set.
1246 Promise<Buffer> promise;
1247 promise.set_value(Buffer(iter->get()));
1248 future = promise.get_future().share();
1250 return future;
1254 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1255 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1256 void ContextImpl::removeBuffer(StringView name)
1258 CheckContext(this);
1260 auto hasher = std::hash<StringView>();
1261 if(UNLIKELY(!mFutureBuffers.empty()))
1263 // If the buffer is already pending for the future, wait for it to
1264 // finish before continuing.
1265 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1266 [hasher](const PendingBuffer &lhs, size_t rhs) -> bool
1267 { return hasher(lhs.mBuffer->getName()) < rhs; }
1269 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1271 iter->mFuture.wait();
1272 mFutureBuffers.erase(iter);
1275 // Clear out any completed futures.
1276 mFutureBuffers.erase(
1277 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1278 [](const PendingBuffer &entry) -> bool
1279 { return GetFutureState(entry.mFuture) == std::future_status::ready; }
1280 ), mFutureBuffers.end()
1284 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1285 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1286 { return hasher(lhs->getName()) < rhs; }
1288 if(iter != mBuffers.end() && (*iter)->getName() == name)
1290 // Remove pending sources whose future was waiting for this buffer.
1291 mPendingSources.erase(
1292 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1293 [iter](PendingSource &entry) -> bool
1295 return (GetFutureState(entry.mFuture) == std::future_status::ready &&
1296 entry.mFuture.get().getHandle() == iter->get());
1298 ), mPendingSources.end()
1300 (*iter)->cleanup();
1301 mBuffers.erase(iter);
1306 ALuint ContextImpl::getSourceId(ALuint maxprio)
1308 ALuint id = 0;
1309 if(mSourceIds.empty())
1311 alGetError();
1312 alGenSources(1, &id);
1313 if(alGetError() == AL_NO_ERROR)
1314 return id;
1316 SourceImpl *lowest = nullptr;
1317 for(SourceBufferUpdateEntry &entry : mPlaySources)
1319 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1320 lowest = entry.mSource;
1322 for(SourceStreamUpdateEntry &entry : mStreamSources)
1324 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1325 lowest = entry.mSource;
1327 if(lowest && lowest->getPriority() < maxprio)
1329 lowest->stop();
1330 if(mMessage.get())
1331 mMessage->sourceForceStopped(lowest);
1334 if(mSourceIds.empty())
1335 throw std::runtime_error("No available sources");
1337 id = mSourceIds.back();
1338 mSourceIds.pop_back();
1339 return id;
1343 DECL_THUNK0(Source, Context, createSource,)
1344 Source ContextImpl::createSource()
1346 CheckContext(this);
1348 SourceImpl *source;
1349 if(!mFreeSources.empty())
1351 source = mFreeSources.back();
1352 mFreeSources.pop_back();
1354 else
1356 mAllSources.emplace_back(this);
1357 source = &mAllSources.back();
1359 return Source(source);
1363 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1365 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1366 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1367 { return lhs.mSource < rhs; }
1369 if(iter == mPendingSources.end() || iter->mSource != source)
1370 mPendingSources.insert(iter, {source, std::move(future)});
1373 void ContextImpl::removePendingSource(SourceImpl *source)
1375 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1376 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1377 { return lhs.mSource < rhs; }
1379 if(iter != mPendingSources.end() && iter->mSource == source)
1380 mPendingSources.erase(iter);
1383 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1385 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1386 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1387 { return lhs.mSource < rhs; }
1389 return (iter != mPendingSources.end() && iter->mSource == source);
1392 void ContextImpl::addFadingSource(SourceImpl *source)
1394 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1395 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1396 { return lhs < rhs; }
1398 if(iter == mFadingSources.end() || *iter != source)
1399 mFadingSources.insert(iter, source);
1402 void ContextImpl::removeFadingSource(SourceImpl *source)
1404 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1405 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1406 { return lhs < rhs; }
1408 if(iter != mFadingSources.end() && *iter == source)
1409 mFadingSources.erase(iter);
1412 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1414 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1415 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1416 { return lhs.mSource < rhs; }
1418 if(iter == mPlaySources.end() || iter->mSource != source)
1419 mPlaySources.insert(iter, {source,id});
1422 void ContextImpl::addPlayingSource(SourceImpl *source)
1424 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1425 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1426 { return lhs.mSource < rhs; }
1428 if(iter == mStreamSources.end() || iter->mSource != source)
1429 mStreamSources.insert(iter, {source});
1432 void ContextImpl::removePlayingSource(SourceImpl *source)
1434 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1435 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1436 { return lhs.mSource < rhs; }
1438 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1439 mPlaySources.erase(iter0);
1440 else
1442 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1443 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1444 { return lhs.mSource < rhs; }
1446 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1447 mStreamSources.erase(iter1);
1452 void ContextImpl::addStream(SourceImpl *source)
1454 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1455 if(mThread.get_id() == std::thread::id())
1456 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1457 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1458 if(iter == mStreamingSources.end() || *iter != source)
1459 mStreamingSources.insert(iter, source);
1462 void ContextImpl::removeStream(SourceImpl *source)
1464 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1465 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1466 if(iter != mStreamingSources.end() && *iter == source)
1467 mStreamingSources.erase(iter);
1470 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1472 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1473 if(iter != mStreamingSources.end() && *iter == source)
1474 mStreamingSources.erase(iter);
1478 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1479 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1481 if(!hasExtension(AL::EXT_EFX) || !alGenAuxiliaryEffectSlots)
1482 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1483 CheckContext(this);
1485 alGetError();
1486 ALuint id = 0;
1487 alGenAuxiliaryEffectSlots(1, &id);
1488 ALenum err = alGetError();
1489 if(err != AL_NO_ERROR)
1490 throw al_error(err, "Failed to create AuxiliaryEffectSlot");
1491 try {
1492 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1494 catch(...) {
1495 alDeleteAuxiliaryEffectSlots(1, &id);
1496 throw;
1501 DECL_THUNK0(Effect, Context, createEffect,)
1502 Effect ContextImpl::createEffect()
1504 if(!hasExtension(AL::EXT_EFX))
1505 throw std::runtime_error("Effects not supported");
1506 CheckContext(this);
1508 alGetError();
1509 ALuint id = 0;
1510 alGenEffects(1, &id);
1511 ALenum err = alGetError();
1512 if(err != AL_NO_ERROR)
1513 throw al_error(err, "Failed to create Effect");
1514 try {
1515 return Effect(new EffectImpl(this, id));
1517 catch(...) {
1518 alDeleteEffects(1, &id);
1519 throw;
1524 DECL_THUNK0(SourceGroup, Context, createSourceGroup,)
1525 SourceGroup ContextImpl::createSourceGroup()
1527 auto srcgroup = MakeUnique<SourceGroupImpl>(this);
1528 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), srcgroup);
1530 iter = mSourceGroups.insert(iter, std::move(srcgroup));
1531 return SourceGroup(iter->get());
1534 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1536 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group,
1537 [](const UniquePtr<SourceGroupImpl> &lhs, SourceGroupImpl *rhs) -> bool
1538 { return lhs.get() < rhs; }
1540 if(iter != mSourceGroups.end() && iter->get() == group)
1541 mSourceGroups.erase(iter);
1545 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1546 void ContextImpl::setDopplerFactor(ALfloat factor)
1548 if(!(factor >= 0.0f))
1549 throw std::out_of_range("Doppler factor out of range");
1550 CheckContext(this);
1551 alDopplerFactor(factor);
1555 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1556 void ContextImpl::setSpeedOfSound(ALfloat speed)
1558 if(!(speed > 0.0f))
1559 throw std::out_of_range("Speed of sound out of range");
1560 CheckContext(this);
1561 alSpeedOfSound(speed);
1565 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1566 void ContextImpl::setDistanceModel(DistanceModel model)
1568 CheckContext(this);
1569 alDistanceModel((ALenum)model);
1573 DECL_THUNK0(void, Context, update,)
1574 void ContextImpl::update()
1576 CheckContext(this);
1577 mPendingSources.erase(
1578 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1579 [](PendingSource &entry) -> bool
1580 { return !entry.mSource->checkPending(entry.mFuture); }
1581 ), mPendingSources.end()
1583 if(!mFadingSources.empty())
1585 auto cur_time = std::chrono::steady_clock::now().time_since_epoch();
1586 mFadingSources.erase(
1587 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1588 [cur_time](SourceImpl *source) -> bool
1589 { return !source->fadeUpdate(cur_time); }
1590 ), mFadingSources.end()
1593 mPlaySources.erase(
1594 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1595 [](const SourceBufferUpdateEntry &entry) -> bool
1596 { return !entry.mSource->playUpdate(entry.mId); }
1597 ), mPlaySources.end()
1599 mStreamSources.erase(
1600 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1601 [](const SourceStreamUpdateEntry &entry) -> bool
1602 { return !entry.mSource->playUpdate(); }
1603 ), mStreamSources.end()
1606 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1608 // For performance reasons, don't wait for the thread's mutex. This
1609 // should be called often enough to keep up with any and all streams
1610 // regardless.
1611 mWakeThread.notify_all();
1614 if(hasExtension(AL::EXT_disconnect) && mIsConnected)
1616 ALCint connected;
1617 alcGetIntegerv(mDevice->getALCdevice(), ALC_CONNECTED, 1, &connected);
1618 mIsConnected = connected;
1619 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1623 DECL_THUNK0(Device, Context, getDevice,)
1624 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1625 DECL_THUNK0(Listener, Context, getListener,)
1626 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1628 void Context::MakeCurrent(Context context)
1629 { ContextImpl::MakeCurrent(context.pImpl); }
1631 Context Context::GetCurrent()
1632 { return Context(ContextImpl::GetCurrent()); }
1634 void Context::MakeThreadCurrent(Context context)
1635 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1637 Context Context::GetThreadCurrent()
1638 { return Context(ContextImpl::GetThreadCurrent()); }
1641 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1642 void ListenerImpl::setGain(ALfloat gain)
1644 if(!(gain >= 0.0f))
1645 throw std::out_of_range("Gain out of range");
1646 CheckContext(mContext);
1647 alListenerf(AL_GAIN, gain);
1651 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, const Vector3Pair&)
1652 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, const std::pair<Vector3,Vector3> &orientation)
1654 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1655 CheckContext(mContext);
1656 Batcher batcher = mContext->getBatcher();
1657 alListenerfv(AL_POSITION, position.getPtr());
1658 alListenerfv(AL_VELOCITY, velocity.getPtr());
1659 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1662 DECL_THUNK1(void, Listener, setPosition,, const Vector3&)
1663 void ListenerImpl::setPosition(const Vector3 &position)
1665 CheckContext(mContext);
1666 alListenerfv(AL_POSITION, position.getPtr());
1669 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1670 void ListenerImpl::setPosition(const ALfloat *pos)
1672 CheckContext(mContext);
1673 alListenerfv(AL_POSITION, pos);
1676 DECL_THUNK1(void, Listener, setVelocity,, const Vector3&)
1677 void ListenerImpl::setVelocity(const Vector3 &velocity)
1679 CheckContext(mContext);
1680 alListenerfv(AL_VELOCITY, velocity.getPtr());
1683 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1684 void ListenerImpl::setVelocity(const ALfloat *vel)
1686 CheckContext(mContext);
1687 alListenerfv(AL_VELOCITY, vel);
1690 DECL_THUNK1(void, Listener, setOrientation,, const Vector3Pair&)
1691 void ListenerImpl::setOrientation(const std::pair<Vector3,Vector3> &orientation)
1693 CheckContext(mContext);
1694 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1697 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1698 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1700 CheckContext(mContext);
1701 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1702 alListenerfv(AL_ORIENTATION, ori);
1705 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1706 void ListenerImpl::setOrientation(const ALfloat *ori)
1708 CheckContext(mContext);
1709 alListenerfv(AL_ORIENTATION, ori);
1712 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)
1713 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1715 if(!(m_u > 0.0f))
1716 throw std::out_of_range("Invalid meters per unit");
1717 CheckContext(mContext);
1718 if(mContext->hasExtension(AL::EXT_EFX))
1719 alListenerf(AL_METERS_PER_UNIT, m_u);