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