Use traits_type::to_int_type instead of casting to unsigned char
[alure.git] / src / context.cpp
blob86ba2b00335da89e49755683b5248012b9b9115b
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 ALExtension extension;
436 const char name[32];
437 void (&loader)(ContextImpl*);
438 } ALExtensionList[] = {
439 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
441 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
442 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
443 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
445 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
446 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
447 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
449 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
450 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
451 { SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
452 { SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
454 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
456 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
457 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
461 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
462 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
464 void ContextImpl::MakeCurrent(ContextImpl *context)
466 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
468 if(alcMakeContextCurrent(context ? context->getContext() : nullptr) == ALC_FALSE)
469 throw std::runtime_error("Call to alcMakeContextCurrent failed");
470 if(context)
472 context->addRef();
473 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
475 std::swap(sCurrentCtx, context);
476 if(context) context->decRef();
478 if(sThreadCurrentCtx)
479 sThreadCurrentCtx->decRef();
480 sThreadCurrentCtx = 0;
482 if((context = sCurrentCtx) != nullptr)
484 ctxlock.unlock();
485 context->mWakeThread.notify_all();
489 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
491 if(!DeviceManagerImpl::SetThreadContext)
492 throw std::runtime_error("Thread-local contexts unsupported");
493 if(DeviceManagerImpl::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
494 throw std::runtime_error("Call to alcSetThreadContext failed");
495 if(context)
497 context->addRef();
498 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
500 if(sThreadCurrentCtx)
501 sThreadCurrentCtx->decRef();
502 sThreadCurrentCtx = context;
505 void ContextImpl::setupExts()
507 ALCdevice *device = mDevice->getDevice();
508 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
509 for(const auto &entry : ALExtensionList)
511 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
512 alcIsExtensionPresent(device, entry.name) :
513 alIsExtensionPresent(entry.name);
514 if(mHasExt[entry.extension]) entry.loader(this);
519 void ContextImpl::backgroundProc()
521 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
522 DeviceManagerImpl::SetThreadContext(getContext());
524 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
525 std::chrono::milliseconds waketime(0);
526 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
527 while(!mQuitThread.load(std::memory_order_acquire))
530 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
531 mStreamingSources.erase(
532 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
533 [](SourceImpl *source) -> bool
534 { return !source->updateAsync(); }
535 ), mStreamingSources.end()
539 // Only do one pending buffer at a time. In case there's several large
540 // buffers to load, we still need to process streaming sources so they
541 // don't underrun.
542 PendingBuffer *lastpb = mPendingCurrent.load(std::memory_order_acquire);
543 if(PendingBuffer *pb = lastpb->mNext.load(std::memory_order_relaxed))
545 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
546 pb->mPromise.set_value(Buffer(pb->mBuffer));
547 Promise<Buffer>().swap(pb->mPromise);
548 mPendingCurrent.store(pb, std::memory_order_release);
549 continue;
552 std::unique_lock<std::mutex> wakelock(mWakeMutex);
553 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
555 ctxlock.unlock();
557 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
558 if(interval.count() == 0)
559 mWakeThread.wait(wakelock);
560 else
562 auto now = std::chrono::steady_clock::now() - basetime;
563 if(now > waketime)
565 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
566 waketime += interval * mult;
567 mWakeThread.wait_until(wakelock, waketime + basetime);
570 wakelock.unlock();
572 ctxlock.lock();
573 while(!mQuitThread.load(std::memory_order_acquire) &&
574 alcGetCurrentContext() != getContext())
575 mWakeThread.wait(ctxlock);
578 ctxlock.unlock();
580 if(DeviceManagerImpl::SetThreadContext)
581 DeviceManagerImpl::SetThreadContext(nullptr);
585 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
586 : mListener(this), mContext(context), mDevice(device),
587 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
588 mRefs(0), mHasExt{false}, mIsConnected(true), mIsBatching(false),
589 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
590 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
591 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
592 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
593 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
594 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
595 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
596 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
597 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
598 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
600 mPendingHead = new PendingBuffer;
601 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
602 mPendingTail = mPendingHead;
605 ContextImpl::~ContextImpl()
607 PendingBuffer *pb = mPendingTail;
608 while(pb)
610 PendingBuffer *next = pb->mNext.load(std::memory_order_relaxed);
611 delete pb;
612 pb = next;
614 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
615 mPendingHead = nullptr;
616 mPendingTail = nullptr;
620 void ContextImpl::destroy()
622 if(mRefs.load() != 0)
623 throw std::runtime_error("Context is in use");
624 if(!mBuffers.empty())
625 throw std::runtime_error("Trying to destroy a context with buffers");
627 if(mThread.joinable())
629 std::unique_lock<std::mutex> lock(mWakeMutex);
630 mQuitThread.store(true, std::memory_order_release);
631 lock.unlock();
632 mWakeThread.notify_all();
633 mThread.join();
636 alcDestroyContext(mContext);
637 mContext = nullptr;
639 mDevice->removeContext(this);
643 void ContextImpl::startBatch()
645 alcSuspendContext(mContext);
646 mIsBatching = true;
649 void ContextImpl::endBatch()
651 alcProcessContext(mContext);
652 mIsBatching = false;
656 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler> handler)
658 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
659 mMessage.swap(handler);
660 return handler;
664 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
666 if(interval.count() < 0 || interval > std::chrono::seconds(1))
667 throw std::runtime_error("Async wake interval out of range");
668 mWakeInterval.store(interval);
669 mWakeMutex.lock(); mWakeMutex.unlock();
670 mWakeThread.notify_all();
674 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
676 DecoderOrExceptT ret;
678 String oldname = String(name);
679 auto file = FileIOFactory::get().openFile(oldname);
680 if(file) return (ret = GetDecoder(name, std::move(file)));
682 // Resource not found. Try to find a substitute.
683 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+oldname));
684 do {
685 String newname(mMessage->resourceNotFound(oldname));
686 if(newname.empty())
687 return (ret = std::runtime_error("Failed to open "+oldname));
688 file = FileIOFactory::get().openFile(newname);
689 oldname = std::move(newname);
690 } while(!file);
692 return (ret = GetDecoder(oldname, std::move(file)));
695 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
697 CheckContext(this);
698 DecoderOrExceptT dec = findDecoder(name);
699 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
700 return *decoder;
701 throw std::get<std::runtime_error>(dec);
705 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
707 CheckContext(this);
708 return GetFormat(channels, type) != AL_NONE;
712 ArrayView<String> ContextImpl::getAvailableResamplers()
714 CheckContext(this);
715 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
717 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
718 mResamplers.reserve(num_resamplers);
719 for(int i = 0;i < num_resamplers;i++)
720 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
721 if(mResamplers.empty())
722 mResamplers.emplace_back();
724 return mResamplers;
727 ALsizei ContextImpl::getDefaultResamplerIndex() const
729 CheckContext(this);
730 if(!hasExtension(SOFT_source_resampler))
731 return 0;
732 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
736 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
738 BufferOrExceptT retval;
739 ALuint srate = decoder->getFrequency();
740 ChannelConfig chans = decoder->getChannelConfig();
741 SampleType type = decoder->getSampleType();
742 ALuint frames = decoder->getLength();
744 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
745 frames = decoder->read(data.data(), frames);
746 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
747 data.resize(FramesToBytes(frames, chans, type));
749 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
750 if(loop_pts.first >= loop_pts.second)
751 loop_pts = std::make_pair(0, frames);
752 else
754 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
755 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
758 // Get the format before calling the bufferLoading message handler, to
759 // ensure it's something OpenAL can handle.
760 ALenum format = GetFormat(chans, type);
761 if(format == AL_NONE)
763 std::stringstream sstr;
764 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
765 return (retval = std::runtime_error(sstr.str()));
768 if(mMessage.get())
769 mMessage->bufferLoading(name, chans, type, srate, data);
771 alGetError();
772 ALuint bid = 0;
773 alGenBuffers(1, &bid);
774 alBufferData(bid, format, data.data(), data.size(), srate);
775 if(hasExtension(SOFT_loop_points))
777 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
778 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
780 if(alGetError() != AL_NO_ERROR)
782 alDeleteBuffers(1, &bid);
783 return (retval = std::runtime_error("Failed to buffer data"));
786 return (retval = mBuffers.insert(iter,
787 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
788 )->get());
791 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
793 BufferOrExceptT retval;
794 ALuint srate = decoder->getFrequency();
795 ChannelConfig chans = decoder->getChannelConfig();
796 SampleType type = decoder->getSampleType();
797 ALuint frames = decoder->getLength();
798 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
800 ALenum format = GetFormat(chans, type);
801 if(format == AL_NONE)
803 std::stringstream sstr;
804 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
805 return (retval = std::runtime_error(sstr.str()));
808 alGetError();
809 ALuint bid = 0;
810 alGenBuffers(1, &bid);
811 if(alGetError() != AL_NO_ERROR)
812 return (retval = std::runtime_error("Failed to create buffer"));
814 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
816 if(mThread.get_id() == std::thread::id())
817 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
819 PendingBuffer *pb = nullptr;
820 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
821 pb = new PendingBuffer{buffer.get(), decoder, format, frames, std::move(promise)};
822 else
824 pb = mPendingTail;
825 pb->mBuffer = buffer.get();
826 pb->mDecoder = decoder;
827 pb->mFormat = format;
828 pb->mFrames = frames;
829 pb->mPromise = std::move(promise);
830 mPendingTail = pb->mNext.exchange(nullptr, std::memory_order_relaxed);
833 mPendingHead->mNext.store(pb, std::memory_order_release);
834 mPendingHead = pb;
836 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
839 Buffer ContextImpl::getBuffer(StringView name)
841 CheckContext(this);
843 auto hasher = std::hash<StringView>();
844 if(Expect<false>(!mFutureBuffers.empty()))
846 Buffer buffer;
848 // If the buffer is already pending for the future, wait for it
849 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
850 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
851 { return hasher(lhs.mBuffer->getName()) < rhs; }
853 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
855 buffer = iter->mFuture.get();
856 mFutureBuffers.erase(iter);
859 // Clear out any completed futures.
860 mFutureBuffers.erase(
861 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
862 [](const PendingFuture &entry) -> bool
863 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
864 ), mFutureBuffers.end()
867 // If we got the buffer, return it. Otherwise, go load it normally.
868 if(buffer) return buffer;
871 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
872 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
873 { return hasher(lhs->getName()) < rhs; }
875 if(iter != mBuffers.end() && (*iter)->getName() == name)
876 return Buffer(iter->get());
878 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
879 Buffer *buffer = std::get_if<Buffer>(&ret);
880 if(Expect<false>(!buffer))
881 throw std::get<std::runtime_error>(ret);
882 return *buffer;
885 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
887 SharedFuture<Buffer> future;
888 CheckContext(this);
890 auto hasher = std::hash<StringView>();
891 if(Expect<false>(!mFutureBuffers.empty()))
893 // Check if the future that's being created already exists
894 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
895 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
896 { return hasher(lhs.mBuffer->getName()) < rhs; }
898 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
900 future = iter->mFuture;
901 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
902 mFutureBuffers.erase(iter);
903 return future;
906 // Clear out any fulfilled futures.
907 mFutureBuffers.erase(
908 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
909 [](const PendingFuture &entry) -> bool
910 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
911 ), mFutureBuffers.end()
915 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
916 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
917 { return hasher(lhs->getName()) < rhs; }
919 if(iter != mBuffers.end() && (*iter)->getName() == name)
921 // User asked to create a future buffer that's already loaded. Just
922 // construct a promise, fulfill the promise immediately, then return a
923 // shared future that's already set.
924 Promise<Buffer> promise;
925 promise.set_value(Buffer(iter->get()));
926 return promise.get_future().share();
929 Promise<Buffer> promise;
930 future = promise.get_future().share();
932 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
933 Buffer *buffer = std::get_if<Buffer>(&ret);
934 if(Expect<false>(!buffer))
935 throw std::get<std::runtime_error>(ret);
936 mWakeMutex.lock(); mWakeMutex.unlock();
937 mWakeThread.notify_all();
939 mFutureBuffers.insert(
940 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
941 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
942 { return hasher(lhs.mBuffer->getName()) < rhs; }
943 ), { buffer->getHandle(), future }
946 return future;
949 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
951 CheckContext(this);
953 if(Expect<false>(!mFutureBuffers.empty()))
955 // Clear out any fulfilled futures.
956 mFutureBuffers.erase(
957 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
958 [](const PendingFuture &entry) -> bool
959 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
960 ), mFutureBuffers.end()
964 auto hasher = std::hash<StringView>();
965 for(const StringView name : names)
967 // Check if the buffer that's being created already exists
968 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
969 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
970 { return hasher(lhs->getName()) < rhs; }
972 if(iter != mBuffers.end() && (*iter)->getName() == name)
973 continue;
975 DecoderOrExceptT dec = findDecoder(name);
976 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
977 if(!decoder) continue;
979 Promise<Buffer> promise;
980 SharedFuture<Buffer> future = promise.get_future().share();
982 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
983 std::move(promise));
984 Buffer *buffer = std::get_if<Buffer>(&buf);
985 if(Expect<false>(!buffer)) continue;
987 mFutureBuffers.insert(
988 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
989 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
990 { return hasher(lhs.mBuffer->getName()) < rhs; }
991 ), { buffer->getHandle(), future }
994 mWakeMutex.lock(); mWakeMutex.unlock();
995 mWakeThread.notify_all();
998 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder> decoder)
1000 CheckContext(this);
1002 auto hasher = std::hash<StringView>();
1003 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1004 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1005 { return hasher(lhs->getName()) < rhs; }
1007 if(iter != mBuffers.end() && (*iter)->getName() == name)
1008 throw std::runtime_error("Buffer \""+name+"\" already exists");
1010 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1011 Buffer *buffer = std::get_if<Buffer>(&ret);
1012 if(Expect<false>(!buffer))
1013 throw std::get<std::runtime_error>(ret);
1014 return *buffer;
1017 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder> decoder)
1019 SharedFuture<Buffer> future;
1020 CheckContext(this);
1022 if(Expect<false>(!mFutureBuffers.empty()))
1024 // Clear out any fulfilled futures.
1025 mFutureBuffers.erase(
1026 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1027 [](const PendingFuture &entry) -> bool
1028 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1029 ), mFutureBuffers.end()
1033 auto hasher = std::hash<StringView>();
1034 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1035 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1036 { return hasher(lhs->getName()) < rhs; }
1038 if(iter != mBuffers.end() && (*iter)->getName() == name)
1039 throw std::runtime_error("Buffer \""+name+"\" already exists");
1041 Promise<Buffer> promise;
1042 future = promise.get_future().share();
1044 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1045 Buffer *buffer = std::get_if<Buffer>(&ret);
1046 if(Expect<false>(!buffer))
1047 throw std::get<std::runtime_error>(ret);
1048 mWakeMutex.lock(); mWakeMutex.unlock();
1049 mWakeThread.notify_all();
1051 mFutureBuffers.insert(
1052 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1053 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1054 { return hasher(lhs.mBuffer->getName()) < rhs; }
1055 ), { buffer->getHandle(), future }
1058 return future;
1062 void ContextImpl::removeBuffer(StringView name)
1064 CheckContext(this);
1066 auto hasher = std::hash<StringView>();
1067 if(Expect<false>(!mFutureBuffers.empty()))
1069 // If the buffer is already pending for the future, wait for it to
1070 // finish before continuing.
1071 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1072 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1073 { return hasher(lhs.mBuffer->getName()) < rhs; }
1075 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1077 iter->mFuture.wait();
1078 mFutureBuffers.erase(iter);
1081 // Clear out any completed futures.
1082 mFutureBuffers.erase(
1083 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1084 [](const PendingFuture &entry) -> bool
1085 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1086 ), mFutureBuffers.end()
1090 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1091 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1092 { return hasher(lhs->getName()) < rhs; }
1094 if(iter != mBuffers.end() && (*iter)->getName() == name)
1096 // Remove pending sources whose future was waiting for this buffer.
1097 mPendingSources.erase(
1098 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1099 [iter](PendingSource &entry) -> bool
1101 return (entry.mFuture.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready &&
1102 entry.mFuture.get().getHandle() == iter->get());
1104 ), mPendingSources.end()
1106 (*iter)->cleanup();
1107 mBuffers.erase(iter);
1112 ALuint ContextImpl::getSourceId(ALuint maxprio)
1114 ALuint id = 0;
1115 if(mSourceIds.empty())
1117 alGetError();
1118 alGenSources(1, &id);
1119 if(alGetError() == AL_NO_ERROR)
1120 return id;
1122 SourceImpl *lowest = nullptr;
1123 for(SourceBufferUpdateEntry &entry : mPlaySources)
1125 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1126 lowest = entry.mSource;
1128 for(SourceStreamUpdateEntry &entry : mStreamSources)
1130 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1131 lowest = entry.mSource;
1133 if(lowest && lowest->getPriority() < maxprio)
1135 lowest->stop();
1136 if(mMessage.get())
1137 mMessage->sourceForceStopped(lowest);
1140 if(mSourceIds.empty())
1141 throw std::runtime_error("No available sources");
1143 id = mSourceIds.top();
1144 mSourceIds.pop();
1145 return id;
1149 Source ContextImpl::createSource()
1151 CheckContext(this);
1153 SourceImpl *source;
1154 if(!mFreeSources.empty())
1156 source = mFreeSources.back();
1157 mFreeSources.pop_back();
1159 else
1161 mAllSources.emplace_back(this);
1162 source = &mAllSources.back();
1164 return Source(source);
1168 void ContextImpl::addPendingSource(SourceImpl *source, SharedFuture<Buffer> future)
1170 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1171 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1172 { return lhs.mSource < rhs; }
1174 if(iter == mPendingSources.end() || iter->mSource != source)
1175 mPendingSources.insert(iter, {source, std::move(future)});
1178 void ContextImpl::removePendingSource(SourceImpl *source)
1180 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1181 [](const PendingSource &lhs, SourceImpl *rhs) -> bool
1182 { return lhs.mSource < rhs; }
1184 if(iter != mPendingSources.end() && iter->mSource == source)
1185 mPendingSources.erase(iter);
1188 bool ContextImpl::isPendingSource(const SourceImpl *source) const
1190 auto iter = std::lower_bound(mPendingSources.begin(), mPendingSources.end(), source,
1191 [](const PendingSource &lhs, const SourceImpl *rhs) -> bool
1192 { return lhs.mSource < rhs; }
1194 return (iter != mPendingSources.end() && iter->mSource == source);
1197 void ContextImpl::addFadingSource(SourceImpl *source)
1199 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1200 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1201 { return lhs < rhs; }
1203 if(iter == mFadingSources.end() || *iter != source)
1204 mFadingSources.insert(iter, source);
1207 void ContextImpl::removeFadingSource(SourceImpl *source)
1209 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1210 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1211 { return lhs < rhs; }
1213 if(iter != mFadingSources.end() && *iter == source)
1214 mFadingSources.erase(iter);
1217 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1219 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1220 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1221 { return lhs.mSource < rhs; }
1223 if(iter == mPlaySources.end() || iter->mSource != source)
1224 mPlaySources.insert(iter, {source,id});
1227 void ContextImpl::addPlayingSource(SourceImpl *source)
1229 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1230 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1231 { return lhs.mSource < rhs; }
1233 if(iter == mStreamSources.end() || iter->mSource != source)
1234 mStreamSources.insert(iter, {source});
1237 void ContextImpl::removePlayingSource(SourceImpl *source)
1239 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1240 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1241 { return lhs.mSource < rhs; }
1243 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1244 mPlaySources.erase(iter0);
1245 else
1247 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1248 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1249 { return lhs.mSource < rhs; }
1251 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1252 mStreamSources.erase(iter1);
1257 void ContextImpl::addStream(SourceImpl *source)
1259 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1260 if(mThread.get_id() == std::thread::id())
1261 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1262 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1263 if(iter == mStreamingSources.end() || *iter != source)
1264 mStreamingSources.insert(iter, source);
1267 void ContextImpl::removeStream(SourceImpl *source)
1269 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1270 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1271 if(iter != mStreamingSources.end() && *iter == source)
1272 mStreamingSources.erase(iter);
1275 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1277 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1278 if(iter != mStreamingSources.end() && *iter == source)
1279 mStreamingSources.erase(iter);
1283 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1285 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
1286 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1287 CheckContext(this);
1289 alGetError();
1290 ALuint id = 0;
1291 alGenAuxiliaryEffectSlots(1, &id);
1292 if(alGetError() != AL_NO_ERROR)
1293 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1294 try {
1295 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1297 catch(...) {
1298 alDeleteAuxiliaryEffectSlots(1, &id);
1299 throw;
1304 Effect ContextImpl::createEffect()
1306 if(!hasExtension(EXT_EFX))
1307 throw std::runtime_error("Effects not supported");
1308 CheckContext(this);
1310 alGetError();
1311 ALuint id = 0;
1312 alGenEffects(1, &id);
1313 if(alGetError() != AL_NO_ERROR)
1314 throw std::runtime_error("Failed to create Effect");
1315 try {
1316 return Effect(new EffectImpl(this, id));
1318 catch(...) {
1319 alDeleteEffects(1, &id);
1320 throw;
1325 SourceGroup ContextImpl::createSourceGroup(StringView name)
1327 auto hasher = std::hash<StringView>();
1328 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1329 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1330 { return hasher(lhs->getName()) < rhs; }
1332 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1333 throw std::runtime_error("Duplicate source group name");
1334 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, String(name)));
1335 return SourceGroup(iter->get());
1338 SourceGroup ContextImpl::getSourceGroup(StringView name)
1340 auto hasher = std::hash<StringView>();
1341 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1342 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1343 { return hasher(lhs->getName()) < rhs; }
1345 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1346 throw std::runtime_error("Source group not found");
1347 return SourceGroup(iter->get());
1350 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1352 auto hasher = std::hash<StringView>();
1353 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(group->getName()),
1354 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1355 { return hasher(lhs->getName()) < rhs; }
1357 if(iter != mSourceGroups.end() && iter->get() == group)
1358 mSourceGroups.erase(iter);
1362 void ContextImpl::setDopplerFactor(ALfloat factor)
1364 if(!(factor >= 0.0f))
1365 throw std::runtime_error("Doppler factor out of range");
1366 CheckContext(this);
1367 alDopplerFactor(factor);
1371 void ContextImpl::setSpeedOfSound(ALfloat speed)
1373 if(!(speed > 0.0f))
1374 throw std::runtime_error("Speed of sound out of range");
1375 CheckContext(this);
1376 alSpeedOfSound(speed);
1380 void ContextImpl::setDistanceModel(DistanceModel model)
1382 CheckContext(this);
1383 alDistanceModel((ALenum)model);
1387 void ContextImpl::update()
1389 CheckContext(this);
1390 mPendingSources.erase(
1391 std::remove_if(mPendingSources.begin(), mPendingSources.end(),
1392 [](PendingSource &entry) -> bool
1393 { return !entry.mSource->checkPending(entry.mFuture); }
1394 ), mPendingSources.end()
1396 if(!mFadingSources.empty())
1398 auto cur_time = std::chrono::steady_clock::now();
1399 mFadingSources.erase(
1400 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1401 [cur_time](SourceImpl *source) -> bool
1402 { return !source->fadeUpdate(cur_time); }
1403 ), mFadingSources.end()
1406 mPlaySources.erase(
1407 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1408 [](const SourceBufferUpdateEntry &entry) -> bool
1409 { return !entry.mSource->playUpdate(entry.mId); }
1410 ), mPlaySources.end()
1412 mStreamSources.erase(
1413 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1414 [](const SourceStreamUpdateEntry &entry) -> bool
1415 { return !entry.mSource->playUpdate(); }
1416 ), mStreamSources.end()
1419 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1421 // For performance reasons, don't wait for the thread's mutex. This
1422 // should be called often enough to keep up with any and all streams
1423 // regardless.
1424 mWakeThread.notify_all();
1427 if(hasExtension(EXT_disconnect) && mIsConnected)
1429 ALCint connected;
1430 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1431 mIsConnected = connected;
1432 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1436 void Context::destroy()
1438 pImpl->destroy();
1439 pImpl = nullptr;
1441 DECL_THUNK0(Device, Context, getDevice,)
1442 DECL_THUNK0(void, Context, startBatch,)
1443 DECL_THUNK0(void, Context, endBatch,)
1444 DECL_THUNK0(Listener, Context, getListener,)
1445 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1446 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1447 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1448 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1449 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
1450 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1451 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1452 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1453 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1454 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1455 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1456 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1457 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1458 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1459 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1460 DECL_THUNK0(Source, Context, createSource,)
1461 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1462 DECL_THUNK0(Effect, Context, createEffect,)
1463 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, StringView)
1464 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, StringView)
1465 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1466 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1467 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1468 DECL_THUNK0(void, Context, update,)
1471 void Context::MakeCurrent(Context context)
1472 { ContextImpl::MakeCurrent(context.pImpl); }
1474 Context Context::GetCurrent()
1475 { return Context(ContextImpl::GetCurrent()); }
1477 void Context::MakeThreadCurrent(Context context)
1478 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1480 Context Context::GetThreadCurrent()
1481 { return Context(ContextImpl::GetThreadCurrent()); }
1484 void ListenerImpl::setGain(ALfloat gain)
1486 if(!(gain >= 0.0f))
1487 throw std::runtime_error("Gain out of range");
1488 CheckContext(mContext);
1489 alListenerf(AL_GAIN, gain);
1493 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1495 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1496 CheckContext(mContext);
1497 Batcher batcher = mContext->getBatcher();
1498 alListenerfv(AL_POSITION, position.getPtr());
1499 alListenerfv(AL_VELOCITY, velocity.getPtr());
1500 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1503 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1505 CheckContext(mContext);
1506 alListener3f(AL_POSITION, x, y, z);
1509 void ListenerImpl::setPosition(const ALfloat *pos)
1511 CheckContext(mContext);
1512 alListenerfv(AL_POSITION, pos);
1515 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1517 CheckContext(mContext);
1518 alListener3f(AL_VELOCITY, x, y, z);
1521 void ListenerImpl::setVelocity(const ALfloat *vel)
1523 CheckContext(mContext);
1524 alListenerfv(AL_VELOCITY, vel);
1527 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1529 CheckContext(mContext);
1530 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1531 alListenerfv(AL_ORIENTATION, ori);
1534 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1536 CheckContext(mContext);
1537 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1538 alListenerfv(AL_ORIENTATION, ori);
1541 void ListenerImpl::setOrientation(const ALfloat *ori)
1543 CheckContext(mContext);
1544 alListenerfv(AL_ORIENTATION, ori);
1547 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1549 if(!(m_u > 0.0f))
1550 throw std::runtime_error("Invalid meters per unit");
1551 CheckContext(mContext);
1552 if(mContext->hasExtension(EXT_EFX))
1553 alListenerf(AL_METERS_PER_UNIT, m_u);
1557 using Vector3Pair = std::pair<Vector3,Vector3>;
1559 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1560 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1561 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1562 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1563 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1564 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1565 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1566 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1567 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1568 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)