Use a separate function to remove a source from fading sources
[alure.git] / src / context.cpp
blobc005896ee8420e69210d996c74fe5f03a59a3180
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 if /*constexpr*/ (sizeof(size_t) == 8)
64 static constexpr size_t hash_offset = 0xcbf29ce484222325;
65 static constexpr size_t hash_prime = 0x100000001b3;
67 size_t val = hash_offset;
68 for(auto ch : str)
69 val = (val^static_cast<unsigned char>(ch)) * hash_prime;
70 return val;
72 else
74 static constexpr size_t hash_offset = 0x811c9dc5;
75 static constexpr size_t hash_prime = 0x1000193;
77 size_t val = hash_offset;
78 for(auto ch : str)
79 val = (val^static_cast<unsigned char>(ch)) * hash_prime;
80 return val;
87 namespace
90 // Global mutex to protect global context changes
91 std::mutex mGlobalCtxMutex;
93 #ifdef _WIN32
94 // Windows' std::ifstream fails with non-ANSI paths since the standard only
95 // specifies names using const char* (or std::string). MSVC has a non-standard
96 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
97 // but not all Windows compilers support it. So we have to make our own istream
98 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
99 class StreamBuf final : public std::streambuf {
100 alure::Array<char_type,4096> mBuffer;
101 HANDLE mFile;
103 int_type underflow() override
105 if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
107 // Read in the next chunk of data, and set the pointers on success
108 DWORD got = 0;
109 if(!ReadFile(mFile, mBuffer.data(), mBuffer.size(), &got, NULL))
110 got = 0;
111 setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
113 if(gptr() == egptr())
114 return traits_type::eof();
115 return traits_type::to_int_type(*gptr());
118 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override
120 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
121 return traits_type::eof();
123 LARGE_INTEGER fpos;
124 switch(whence)
126 case std::ios_base::beg:
127 fpos.QuadPart = offset;
128 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
129 return traits_type::eof();
130 break;
132 case std::ios_base::cur:
133 // If the offset remains in the current buffer range, just
134 // update the pointer.
135 if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
136 (offset < 0 && -offset <= off_type(gptr()-eback())))
138 // Get the current file offset to report the correct read
139 // offset.
140 fpos.QuadPart = 0;
141 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
142 return traits_type::eof();
143 setg(eback(), gptr()+offset, egptr());
144 return fpos.QuadPart - off_type(egptr()-gptr());
146 // Need to offset for the file offset being at egptr() while
147 // the requested offset is relative to gptr().
148 offset -= off_type(egptr()-gptr());
149 fpos.QuadPart = offset;
150 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
151 return traits_type::eof();
152 break;
154 case std::ios_base::end:
155 fpos.QuadPart = offset;
156 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
157 return traits_type::eof();
158 break;
160 default:
161 return traits_type::eof();
163 setg(0, 0, 0);
164 return fpos.QuadPart;
167 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
169 // Simplified version of seekoff
170 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
171 return traits_type::eof();
173 LARGE_INTEGER fpos;
174 fpos.QuadPart = pos;
175 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
176 return traits_type::eof();
178 setg(0, 0, 0);
179 return fpos.QuadPart;
182 public:
183 bool open(const char *filename)
185 alure::Vector<wchar_t> wname;
186 int wnamelen;
188 wnamelen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
189 if(wnamelen <= 0) return false;
191 wname.resize(wnamelen);
192 MultiByteToWideChar(CP_UTF8, 0, filename, -1, wname.data(), wnamelen);
194 mFile = CreateFileW(wname.data(), GENERIC_READ, FILE_SHARE_READ, NULL,
195 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
196 if(mFile == INVALID_HANDLE_VALUE) return false;
197 return true;
200 bool is_open() const { return mFile != INVALID_HANDLE_VALUE; }
202 StreamBuf() : mFile(INVALID_HANDLE_VALUE)
204 ~StreamBuf() override
206 if(mFile != INVALID_HANDLE_VALUE)
207 CloseHandle(mFile);
208 mFile = INVALID_HANDLE_VALUE;
212 // Inherit from std::istream to use our custom streambuf
213 class Stream final : public std::istream {
214 public:
215 Stream(const char *filename) : std::istream(new StreamBuf())
217 // Set the failbit if the file failed to open.
218 if(!(static_cast<StreamBuf*>(rdbuf())->open(filename)))
219 clear(failbit);
221 ~Stream() override
222 { delete rdbuf(); }
224 bool is_open() const { return static_cast<StreamBuf*>(rdbuf())->is_open(); }
226 #endif
228 using DecoderEntryPair = std::pair<alure::String,alure::UniquePtr<alure::DecoderFactory>>;
229 const DecoderEntryPair sDefaultDecoders[] = {
230 { "_alure_int_wave", alure::MakeUnique<alure::WaveDecoderFactory>() },
232 #ifdef HAVE_VORBISFILE
233 { "_alure_int_vorbis", alure::MakeUnique<alure::VorbisFileDecoderFactory>() },
234 #endif
235 #ifdef HAVE_LIBFLAC
236 { "_alure_int_flac", alure::MakeUnique<alure::FlacDecoderFactory>() },
237 #endif
238 #ifdef HAVE_OPUSFILE
239 { "_alure_int_opus", alure::MakeUnique<alure::OpusFileDecoderFactory>() },
240 #endif
241 #ifdef HAVE_LIBSNDFILE
242 { "_alure_int_sndfile", alure::MakeUnique<alure::SndFileDecoderFactory>() },
243 #endif
244 #ifdef HAVE_MPG123
245 { "_alure_int_mpg123", alure::MakeUnique<alure::Mpg123DecoderFactory>() },
246 #endif
248 alure::Vector<DecoderEntryPair> sDecoders;
251 template<typename T>
252 alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> &file,
253 T start, T end)
255 alure::DecoderOrExceptT ret;
256 while(start != end)
258 alure::DecoderFactory *factory = start->second.get();
259 auto decoder = factory->createDecoder(file);
260 if(decoder) return (ret = std::move(decoder));
262 if(!file || !(file->clear(),file->seekg(0)))
263 return (ret = std::runtime_error("Failed to rewind "+name+" for the next decoder factory"));
265 ++start;
268 return (ret = nullptr);
271 static alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> file)
273 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
274 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
275 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
276 decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
277 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
278 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
279 return (decoder = std::runtime_error("No decoder for "+name));
282 class DefaultFileIOFactory final : public alure::FileIOFactory {
283 alure::UniquePtr<std::istream> openFile(const alure::String &name) override
285 #ifdef _WIN32
286 auto file = alure::MakeUnique<Stream>(name.c_str());
287 #else
288 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
289 #endif
290 if(!file->is_open()) file = nullptr;
291 return std::move(file);
294 DefaultFileIOFactory sDefaultFileFactory;
296 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
300 namespace alure
303 Decoder::~Decoder() { }
304 DecoderFactory::~DecoderFactory() { }
306 void RegisterDecoder(StringView name, UniquePtr<DecoderFactory> factory)
308 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
309 [](const DecoderEntryPair &entry, StringView rhs) -> bool
310 { return entry.first < rhs; }
312 if(iter != sDecoders.end())
313 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
314 sDecoders.insert(iter, std::make_pair(String(name), std::move(factory)));
317 UniquePtr<DecoderFactory> UnregisterDecoder(StringView name)
319 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
320 [](const DecoderEntryPair &entry, StringView rhs) -> bool
321 { return entry.first < rhs; }
323 if(iter != sDecoders.end())
325 UniquePtr<DecoderFactory> factory = std::move(iter->second);
326 sDecoders.erase(iter);
327 return factory;
329 return nullptr;
333 FileIOFactory::~FileIOFactory() { }
335 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
337 std::swap(sFileFactory, factory);
338 return factory;
341 FileIOFactory &FileIOFactory::get()
343 FileIOFactory *factory = sFileFactory.get();
344 if(factory) return *factory;
345 return sDefaultFileFactory;
349 // Default message handler methods are no-ops.
350 MessageHandler::~MessageHandler()
354 void MessageHandler::deviceDisconnected(Device)
358 void MessageHandler::sourceStopped(Source)
362 void MessageHandler::sourceForceStopped(Source)
366 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>)
370 String MessageHandler::resourceNotFound(StringView)
372 return String();
376 template<typename T>
377 static inline void LoadALFunc(T **func, const char *name)
378 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
380 static void LoadNothing(ContextImpl*) { }
382 static void LoadEFX(ContextImpl *ctx)
384 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
385 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
386 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
387 LoadALFunc(&ctx->alEffecti, "alEffecti");
388 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
389 LoadALFunc(&ctx->alEffectf, "alEffectf");
390 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
391 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
392 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
393 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
394 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
396 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
397 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
398 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
399 LoadALFunc(&ctx->alFilteri, "alFilteri");
400 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
401 LoadALFunc(&ctx->alFilterf, "alFilterf");
402 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
403 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
404 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
405 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
406 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
408 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
409 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
410 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
411 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
412 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
413 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
414 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
415 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
416 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
417 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
418 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
421 static void LoadSourceResampler(ContextImpl *ctx)
423 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
426 static void LoadSourceLatency(ContextImpl *ctx)
428 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
429 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
432 static const struct {
433 enum ALExtension extension;
434 const char name[32];
435 void (&loader)(ContextImpl*);
436 } ALExtensionList[] = {
437 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
439 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
440 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
441 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
443 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
444 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
445 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
447 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
448 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
449 { SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
450 { SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
452 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
454 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
455 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
459 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
460 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
462 void ContextImpl::MakeCurrent(ContextImpl *context)
464 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
466 if(alcMakeContextCurrent(context ? context->getContext() : nullptr) == ALC_FALSE)
467 throw std::runtime_error("Call to alcMakeContextCurrent failed");
468 if(context)
470 context->addRef();
471 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
473 std::swap(sCurrentCtx, context);
474 if(context) context->decRef();
476 if(sThreadCurrentCtx)
477 sThreadCurrentCtx->decRef();
478 sThreadCurrentCtx = 0;
480 if((context = sCurrentCtx) != nullptr)
482 ctxlock.unlock();
483 context->mWakeThread.notify_all();
487 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
489 if(!DeviceManagerImpl::SetThreadContext)
490 throw std::runtime_error("Thread-local contexts unsupported");
491 if(DeviceManagerImpl::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
492 throw std::runtime_error("Call to alcSetThreadContext failed");
493 if(context)
495 context->addRef();
496 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
498 if(sThreadCurrentCtx)
499 sThreadCurrentCtx->decRef();
500 sThreadCurrentCtx = context;
503 void ContextImpl::setupExts()
505 ALCdevice *device = mDevice->getDevice();
506 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
507 for(const auto &entry : ALExtensionList)
509 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
510 alcIsExtensionPresent(device, entry.name) :
511 alIsExtensionPresent(entry.name);
512 if(mHasExt[entry.extension]) entry.loader(this);
517 void ContextImpl::backgroundProc()
519 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
520 DeviceManagerImpl::SetThreadContext(getContext());
522 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
523 std::chrono::milliseconds waketime(0);
524 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
525 while(!mQuitThread.load(std::memory_order_acquire))
528 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
529 mStreamingSources.erase(
530 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
531 [](SourceImpl *source) -> bool
532 { return !source->updateAsync(); }
533 ), mStreamingSources.end()
537 // Only do one pending buffer at a time. In case there's several large
538 // buffers to load, we still need to process streaming sources so they
539 // don't underrun.
540 PendingBuffer *lastpb = mPendingCurrent.load(std::memory_order_acquire);
541 if(PendingBuffer *pb = lastpb->mNext.load(std::memory_order_relaxed))
543 pb->mBuffer->load(pb->mFrames, pb->mFormat, std::move(pb->mDecoder), this);
544 pb->mPromise.set_value(Buffer(pb->mBuffer));
545 Promise<Buffer>().swap(pb->mPromise);
546 mPendingCurrent.store(pb, std::memory_order_release);
547 continue;
550 std::unique_lock<std::mutex> wakelock(mWakeMutex);
551 if(!mQuitThread.load(std::memory_order_acquire) && lastpb->mNext.load(std::memory_order_acquire) == nullptr)
553 ctxlock.unlock();
555 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
556 if(interval.count() == 0)
557 mWakeThread.wait(wakelock);
558 else
560 auto now = std::chrono::steady_clock::now() - basetime;
561 if(now > waketime)
563 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
564 waketime += interval * mult;
565 mWakeThread.wait_until(wakelock, waketime + basetime);
568 wakelock.unlock();
570 ctxlock.lock();
571 while(!mQuitThread.load(std::memory_order_acquire) &&
572 alcGetCurrentContext() != getContext())
573 mWakeThread.wait(ctxlock);
576 ctxlock.unlock();
578 if(DeviceManagerImpl::SetThreadContext)
579 DeviceManagerImpl::SetThreadContext(nullptr);
583 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
584 : mListener(this), mContext(context), mDevice(device),
585 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
586 mRefs(0), mHasExt{false}, mIsConnected(true), mIsBatching(false),
587 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
588 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
589 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
590 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
591 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
592 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
593 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
594 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
595 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
596 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
598 mPendingHead = new PendingBuffer;
599 mPendingCurrent.store(mPendingHead, std::memory_order_relaxed);
600 mPendingTail = mPendingHead;
603 ContextImpl::~ContextImpl()
605 PendingBuffer *pb = mPendingTail;
606 while(pb)
608 PendingBuffer *next = pb->mNext.load(std::memory_order_relaxed);
609 delete pb;
610 pb = next;
612 mPendingCurrent.store(nullptr, std::memory_order_relaxed);
613 mPendingHead = nullptr;
614 mPendingTail = nullptr;
618 void ContextImpl::destroy()
620 if(mRefs.load() != 0)
621 throw std::runtime_error("Context is in use");
622 if(!mBuffers.empty())
623 throw std::runtime_error("Trying to destroy a context with buffers");
625 if(mThread.joinable())
627 std::unique_lock<std::mutex> lock(mWakeMutex);
628 mQuitThread.store(true, std::memory_order_release);
629 lock.unlock();
630 mWakeThread.notify_all();
631 mThread.join();
634 alcDestroyContext(mContext);
635 mContext = nullptr;
637 mDevice->removeContext(this);
641 void ContextImpl::startBatch()
643 alcSuspendContext(mContext);
644 mIsBatching = true;
647 void ContextImpl::endBatch()
649 alcProcessContext(mContext);
650 mIsBatching = false;
654 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler> handler)
656 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
657 mMessage.swap(handler);
658 return handler;
662 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
664 if(interval.count() < 0 || interval > std::chrono::seconds(1))
665 throw std::runtime_error("Async wake interval out of range");
666 mWakeInterval.store(interval);
667 mWakeMutex.lock(); mWakeMutex.unlock();
668 mWakeThread.notify_all();
672 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
674 DecoderOrExceptT ret;
676 String oldname = String(name);
677 auto file = FileIOFactory::get().openFile(oldname);
678 if(file) return (ret = GetDecoder(name, std::move(file)));
680 // Resource not found. Try to find a substitute.
681 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+oldname));
682 do {
683 String newname(mMessage->resourceNotFound(oldname));
684 if(newname.empty())
685 return (ret = std::runtime_error("Failed to open "+oldname));
686 file = FileIOFactory::get().openFile(newname);
687 oldname = std::move(newname);
688 } while(!file);
690 return (ret = GetDecoder(oldname, std::move(file)));
693 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
695 CheckContext(this);
696 DecoderOrExceptT dec = findDecoder(name);
697 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
698 return *decoder;
699 throw std::get<std::runtime_error>(dec);
703 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
705 CheckContext(this);
706 return GetFormat(channels, type) != AL_NONE;
710 ArrayView<String> ContextImpl::getAvailableResamplers()
712 CheckContext(this);
713 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
715 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
716 mResamplers.reserve(num_resamplers);
717 for(int i = 0;i < num_resamplers;i++)
718 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
719 if(mResamplers.empty())
720 mResamplers.emplace_back();
722 return mResamplers;
725 ALsizei ContextImpl::getDefaultResamplerIndex() const
727 CheckContext(this);
728 if(!hasExtension(SOFT_source_resampler))
729 return 0;
730 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
734 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
736 BufferOrExceptT retval;
737 ALuint srate = decoder->getFrequency();
738 ChannelConfig chans = decoder->getChannelConfig();
739 SampleType type = decoder->getSampleType();
740 ALuint frames = decoder->getLength();
742 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
743 frames = decoder->read(data.data(), frames);
744 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
745 data.resize(FramesToBytes(frames, chans, type));
747 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
748 if(loop_pts.first >= loop_pts.second)
749 loop_pts = std::make_pair(0, frames);
750 else
752 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
753 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
756 // Get the format before calling the bufferLoading message handler, to
757 // ensure it's something OpenAL can handle.
758 ALenum format = GetFormat(chans, type);
759 if(format == AL_NONE)
761 std::stringstream sstr;
762 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
763 return (retval = std::runtime_error(sstr.str()));
766 if(mMessage.get())
767 mMessage->bufferLoading(name, chans, type, srate, data);
769 alGetError();
770 ALuint bid = 0;
771 alGenBuffers(1, &bid);
772 alBufferData(bid, format, data.data(), data.size(), srate);
773 if(hasExtension(SOFT_loop_points))
775 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
776 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
778 if(alGetError() != AL_NO_ERROR)
780 alDeleteBuffers(1, &bid);
781 return (retval = std::runtime_error("Failed to buffer data"));
784 return (retval = mBuffers.insert(iter,
785 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
786 )->get());
789 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
791 BufferOrExceptT retval;
792 ALuint srate = decoder->getFrequency();
793 ChannelConfig chans = decoder->getChannelConfig();
794 SampleType type = decoder->getSampleType();
795 ALuint frames = decoder->getLength();
796 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
798 ALenum format = GetFormat(chans, type);
799 if(format == AL_NONE)
801 std::stringstream sstr;
802 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
803 return (retval = std::runtime_error(sstr.str()));
806 alGetError();
807 ALuint bid = 0;
808 alGenBuffers(1, &bid);
809 if(alGetError() != AL_NO_ERROR)
810 return (retval = std::runtime_error("Failed to create buffer"));
812 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
814 if(mThread.get_id() == std::thread::id())
815 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
817 PendingBuffer *pb = nullptr;
818 if(mPendingTail == mPendingCurrent.load(std::memory_order_acquire))
819 pb = new PendingBuffer{buffer.get(), decoder, format, frames, std::move(promise)};
820 else
822 pb = mPendingTail;
823 pb->mBuffer = buffer.get();
824 pb->mDecoder = decoder;
825 pb->mFormat = format;
826 pb->mFrames = frames;
827 pb->mPromise = std::move(promise);
828 mPendingTail = pb->mNext.exchange(nullptr, std::memory_order_relaxed);
831 mPendingHead->mNext.store(pb, std::memory_order_release);
832 mPendingHead = pb;
834 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
837 Buffer ContextImpl::getBuffer(StringView name)
839 CheckContext(this);
841 auto hasher = std::hash<StringView>();
842 if(Expect<false>(!mFutureBuffers.empty()))
844 Buffer buffer;
846 // If the buffer is already pending for the future, wait for it
847 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
848 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
849 { return hasher(lhs.mBuffer->getName()) < rhs; }
851 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
853 buffer = iter->mFuture.get();
854 mFutureBuffers.erase(iter);
857 // Clear out any completed futures.
858 mFutureBuffers.erase(
859 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
860 [](const PendingFuture &entry) -> bool
861 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
862 ), mFutureBuffers.end()
865 // If we got the buffer, return it. Otherwise, go load it normally.
866 if(buffer) return buffer;
869 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
870 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
871 { return hasher(lhs->getName()) < rhs; }
873 if(iter != mBuffers.end() && (*iter)->getName() == name)
874 return Buffer(iter->get());
876 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
877 Buffer *buffer = std::get_if<Buffer>(&ret);
878 if(Expect<false>(!buffer))
879 throw std::get<std::runtime_error>(ret);
880 return *buffer;
883 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
885 SharedFuture<Buffer> future;
886 CheckContext(this);
888 auto hasher = std::hash<StringView>();
889 if(Expect<false>(!mFutureBuffers.empty()))
891 // Check if the future that's being created already exists
892 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
893 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
894 { return hasher(lhs.mBuffer->getName()) < rhs; }
896 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
898 future = iter->mFuture;
899 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
900 mFutureBuffers.erase(iter);
901 return future;
904 // Clear out any fulfilled futures.
905 mFutureBuffers.erase(
906 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
907 [](const PendingFuture &entry) -> bool
908 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
909 ), mFutureBuffers.end()
913 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
914 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
915 { return hasher(lhs->getName()) < rhs; }
917 if(iter != mBuffers.end() && (*iter)->getName() == name)
919 // User asked to create a future buffer that's already loaded. Just
920 // construct a promise, fulfill the promise immediately, then return a
921 // shared future that's already set.
922 Promise<Buffer> promise;
923 promise.set_value(Buffer(iter->get()));
924 return promise.get_future().share();
927 Promise<Buffer> promise;
928 future = promise.get_future().share();
930 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
931 Buffer *buffer = std::get_if<Buffer>(&ret);
932 if(Expect<false>(!buffer))
933 throw std::get<std::runtime_error>(ret);
934 mWakeMutex.lock(); mWakeMutex.unlock();
935 mWakeThread.notify_all();
937 mFutureBuffers.insert(
938 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
939 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
940 { return hasher(lhs.mBuffer->getName()) < rhs; }
941 ), { buffer->getHandle(), future }
944 return future;
947 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
949 CheckContext(this);
951 if(Expect<false>(!mFutureBuffers.empty()))
953 // Clear out any fulfilled futures.
954 mFutureBuffers.erase(
955 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
956 [](const PendingFuture &entry) -> bool
957 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
958 ), mFutureBuffers.end()
962 auto hasher = std::hash<StringView>();
963 for(const StringView name : names)
965 // Check if the buffer that's being created already exists
966 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
967 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
968 { return hasher(lhs->getName()) < rhs; }
970 if(iter != mBuffers.end() && (*iter)->getName() == name)
971 continue;
973 DecoderOrExceptT dec = findDecoder(name);
974 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
975 if(!decoder) continue;
977 Promise<Buffer> promise;
978 SharedFuture<Buffer> future = promise.get_future().share();
980 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
981 std::move(promise));
982 Buffer *buffer = std::get_if<Buffer>(&buf);
983 if(Expect<false>(!buffer)) continue;
985 mFutureBuffers.insert(
986 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
987 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
988 { return hasher(lhs.mBuffer->getName()) < rhs; }
989 ), { buffer->getHandle(), future }
992 mWakeMutex.lock(); mWakeMutex.unlock();
993 mWakeThread.notify_all();
996 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder> decoder)
998 CheckContext(this);
1000 auto hasher = std::hash<StringView>();
1001 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1002 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1003 { return hasher(lhs->getName()) < rhs; }
1005 if(iter != mBuffers.end() && (*iter)->getName() == name)
1006 throw std::runtime_error("Buffer \""+name+"\" already exists");
1008 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1009 Buffer *buffer = std::get_if<Buffer>(&ret);
1010 if(Expect<false>(!buffer))
1011 throw std::get<std::runtime_error>(ret);
1012 return *buffer;
1015 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder> decoder)
1017 SharedFuture<Buffer> future;
1018 CheckContext(this);
1020 if(Expect<false>(!mFutureBuffers.empty()))
1022 // Clear out any fulfilled futures.
1023 mFutureBuffers.erase(
1024 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1025 [](const PendingFuture &entry) -> bool
1026 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1027 ), mFutureBuffers.end()
1031 auto hasher = std::hash<StringView>();
1032 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1033 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1034 { return hasher(lhs->getName()) < rhs; }
1036 if(iter != mBuffers.end() && (*iter)->getName() == name)
1037 throw std::runtime_error("Buffer \""+name+"\" already exists");
1039 Promise<Buffer> promise;
1040 future = promise.get_future().share();
1042 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1043 Buffer *buffer = std::get_if<Buffer>(&ret);
1044 if(Expect<false>(!buffer))
1045 throw std::get<std::runtime_error>(ret);
1046 mWakeMutex.lock(); mWakeMutex.unlock();
1047 mWakeThread.notify_all();
1049 mFutureBuffers.insert(
1050 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1051 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1052 { return hasher(lhs.mBuffer->getName()) < rhs; }
1053 ), { buffer->getHandle(), future }
1056 return future;
1060 void ContextImpl::removeBuffer(StringView name)
1062 CheckContext(this);
1064 auto hasher = std::hash<StringView>();
1065 if(Expect<false>(!mFutureBuffers.empty()))
1067 // If the buffer is already pending for the future, wait for it to
1068 // finish before continuing.
1069 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1070 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1071 { return hasher(lhs.mBuffer->getName()) < rhs; }
1073 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1075 iter->mFuture.wait();
1076 mFutureBuffers.erase(iter);
1079 // Clear out any completed futures.
1080 mFutureBuffers.erase(
1081 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1082 [](const PendingFuture &entry) -> bool
1083 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1084 ), mFutureBuffers.end()
1088 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1089 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1090 { return hasher(lhs->getName()) < rhs; }
1092 if(iter != mBuffers.end() && (*iter)->getName() == name)
1094 (*iter)->cleanup();
1095 mBuffers.erase(iter);
1100 ALuint ContextImpl::getSourceId(ALuint maxprio)
1102 ALuint id = 0;
1103 if(mSourceIds.empty())
1105 alGetError();
1106 alGenSources(1, &id);
1107 if(alGetError() == AL_NO_ERROR)
1108 return id;
1110 SourceImpl *lowest = nullptr;
1111 for(SourceBufferUpdateEntry &entry : mPlaySources)
1113 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1114 lowest = entry.mSource;
1116 for(SourceStreamUpdateEntry &entry : mStreamSources)
1118 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1119 lowest = entry.mSource;
1121 if(lowest && lowest->getPriority() < maxprio)
1123 lowest->stop();
1124 if(mMessage.get())
1125 mMessage->sourceForceStopped(lowest);
1128 if(mSourceIds.empty())
1129 throw std::runtime_error("No available sources");
1131 id = mSourceIds.top();
1132 mSourceIds.pop();
1133 return id;
1137 Source ContextImpl::createSource()
1139 CheckContext(this);
1141 SourceImpl *source;
1142 if(!mFreeSources.empty())
1144 source = mFreeSources.back();
1145 mFreeSources.pop_back();
1147 else
1149 mAllSources.emplace_back(this);
1150 source = &mAllSources.back();
1152 return Source(source);
1156 void ContextImpl::addFadingSource(SourceImpl *source)
1158 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1159 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1160 { return lhs < rhs; }
1162 if(iter == mFadingSources.end() || *iter != source)
1163 mFadingSources.insert(iter, source);
1166 void ContextImpl::removeFadingSource(SourceImpl *source)
1168 auto iter = std::lower_bound(mFadingSources.begin(), mFadingSources.end(), source,
1169 [](SourceImpl *lhs, SourceImpl *rhs) -> bool
1170 { return lhs < rhs; }
1172 if(iter != mFadingSources.end() && *iter == source)
1173 mFadingSources.erase(iter);
1176 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1178 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1179 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1180 { return lhs.mSource < rhs; }
1182 if(iter == mPlaySources.end() || iter->mSource != source)
1183 mPlaySources.insert(iter, {source,id});
1186 void ContextImpl::addPlayingSource(SourceImpl *source)
1188 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1189 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1190 { return lhs.mSource < rhs; }
1192 if(iter == mStreamSources.end() || iter->mSource != source)
1193 mStreamSources.insert(iter, {source});
1196 void ContextImpl::removePlayingSource(SourceImpl *source)
1198 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1199 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1200 { return lhs.mSource < rhs; }
1202 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1203 mPlaySources.erase(iter0);
1204 else
1206 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1207 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1208 { return lhs.mSource < rhs; }
1210 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1211 mStreamSources.erase(iter1);
1216 void ContextImpl::addStream(SourceImpl *source)
1218 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1219 if(mThread.get_id() == std::thread::id())
1220 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1221 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1222 if(iter == mStreamingSources.end() || *iter != source)
1223 mStreamingSources.insert(iter, source);
1226 void ContextImpl::removeStream(SourceImpl *source)
1228 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1229 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1230 if(iter != mStreamingSources.end() && *iter == source)
1231 mStreamingSources.erase(iter);
1234 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1236 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1237 if(iter != mStreamingSources.end() && *iter == source)
1238 mStreamingSources.erase(iter);
1242 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1244 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
1245 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1246 CheckContext(this);
1248 alGetError();
1249 ALuint id = 0;
1250 alGenAuxiliaryEffectSlots(1, &id);
1251 if(alGetError() != AL_NO_ERROR)
1252 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1253 try {
1254 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1256 catch(...) {
1257 alDeleteAuxiliaryEffectSlots(1, &id);
1258 throw;
1263 Effect ContextImpl::createEffect()
1265 if(!hasExtension(EXT_EFX))
1266 throw std::runtime_error("Effects not supported");
1267 CheckContext(this);
1269 alGetError();
1270 ALuint id = 0;
1271 alGenEffects(1, &id);
1272 if(alGetError() != AL_NO_ERROR)
1273 throw std::runtime_error("Failed to create Effect");
1274 try {
1275 return Effect(new EffectImpl(this, id));
1277 catch(...) {
1278 alDeleteEffects(1, &id);
1279 throw;
1284 SourceGroup ContextImpl::createSourceGroup(StringView name)
1286 auto hasher = std::hash<StringView>();
1287 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1288 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1289 { return hasher(lhs->getName()) < rhs; }
1291 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1292 throw std::runtime_error("Duplicate source group name");
1293 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, String(name)));
1294 return SourceGroup(iter->get());
1297 SourceGroup ContextImpl::getSourceGroup(StringView name)
1299 auto hasher = std::hash<StringView>();
1300 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1301 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1302 { return hasher(lhs->getName()) < rhs; }
1304 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1305 throw std::runtime_error("Source group not found");
1306 return SourceGroup(iter->get());
1309 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1311 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1312 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1313 { return lhs->getName() < rhs; }
1315 if(iter != mSourceGroups.end() && iter->get() == group)
1316 mSourceGroups.erase(iter);
1320 void ContextImpl::setDopplerFactor(ALfloat factor)
1322 if(!(factor >= 0.0f))
1323 throw std::runtime_error("Doppler factor out of range");
1324 CheckContext(this);
1325 alDopplerFactor(factor);
1329 void ContextImpl::setSpeedOfSound(ALfloat speed)
1331 if(!(speed > 0.0f))
1332 throw std::runtime_error("Speed of sound out of range");
1333 CheckContext(this);
1334 alSpeedOfSound(speed);
1338 void ContextImpl::setDistanceModel(DistanceModel model)
1340 CheckContext(this);
1341 alDistanceModel((ALenum)model);
1345 void ContextImpl::update()
1347 CheckContext(this);
1348 if(!mFadingSources.empty())
1350 auto cur_time = std::chrono::steady_clock::now();
1351 mFadingSources.erase(
1352 std::remove_if(mFadingSources.begin(), mFadingSources.end(),
1353 [cur_time](SourceImpl *source) -> bool
1354 { return !source->fadeUpdate(cur_time); }
1355 ), mFadingSources.end()
1358 mPlaySources.erase(
1359 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1360 [](const SourceBufferUpdateEntry &entry) -> bool
1361 { return !entry.mSource->playUpdate(entry.mId); }
1362 ), mPlaySources.end()
1364 mStreamSources.erase(
1365 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1366 [](const SourceStreamUpdateEntry &entry) -> bool
1367 { return !entry.mSource->playUpdate(); }
1368 ), mStreamSources.end()
1371 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1373 // For performance reasons, don't wait for the thread's mutex. This
1374 // should be called often enough to keep up with any and all streams
1375 // regardless.
1376 mWakeThread.notify_all();
1379 if(hasExtension(EXT_disconnect) && mIsConnected)
1381 ALCint connected;
1382 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1383 mIsConnected = connected;
1384 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1388 void Context::destroy()
1390 pImpl->destroy();
1391 pImpl = nullptr;
1393 DECL_THUNK0(Device, Context, getDevice,)
1394 DECL_THUNK0(void, Context, startBatch,)
1395 DECL_THUNK0(void, Context, endBatch,)
1396 DECL_THUNK0(Listener, Context, getListener,)
1397 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1398 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1399 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1400 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1401 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
1402 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1403 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1404 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1405 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1406 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1407 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1408 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1409 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1410 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1411 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1412 DECL_THUNK0(Source, Context, createSource,)
1413 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1414 DECL_THUNK0(Effect, Context, createEffect,)
1415 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, StringView)
1416 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, StringView)
1417 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1418 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1419 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1420 DECL_THUNK0(void, Context, update,)
1423 void Context::MakeCurrent(Context context)
1424 { ContextImpl::MakeCurrent(context.pImpl); }
1426 Context Context::GetCurrent()
1427 { return Context(ContextImpl::GetCurrent()); }
1429 void Context::MakeThreadCurrent(Context context)
1430 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1432 Context Context::GetThreadCurrent()
1433 { return Context(ContextImpl::GetThreadCurrent()); }
1436 void ListenerImpl::setGain(ALfloat gain)
1438 if(!(gain >= 0.0f))
1439 throw std::runtime_error("Gain out of range");
1440 CheckContext(mContext);
1441 alListenerf(AL_GAIN, gain);
1445 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1447 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1448 CheckContext(mContext);
1449 Batcher batcher = mContext->getBatcher();
1450 alListenerfv(AL_POSITION, position.getPtr());
1451 alListenerfv(AL_VELOCITY, velocity.getPtr());
1452 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1455 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1457 CheckContext(mContext);
1458 alListener3f(AL_POSITION, x, y, z);
1461 void ListenerImpl::setPosition(const ALfloat *pos)
1463 CheckContext(mContext);
1464 alListenerfv(AL_POSITION, pos);
1467 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1469 CheckContext(mContext);
1470 alListener3f(AL_VELOCITY, x, y, z);
1473 void ListenerImpl::setVelocity(const ALfloat *vel)
1475 CheckContext(mContext);
1476 alListenerfv(AL_VELOCITY, vel);
1479 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1481 CheckContext(mContext);
1482 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1483 alListenerfv(AL_ORIENTATION, ori);
1486 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1488 CheckContext(mContext);
1489 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1490 alListenerfv(AL_ORIENTATION, ori);
1493 void ListenerImpl::setOrientation(const ALfloat *ori)
1495 CheckContext(mContext);
1496 alListenerfv(AL_ORIENTATION, ori);
1499 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1501 if(!(m_u > 0.0f))
1502 throw std::runtime_error("Invalid meters per unit");
1503 CheckContext(mContext);
1504 if(mContext->hasExtension(EXT_EFX))
1505 alListenerf(AL_METERS_PER_UNIT, m_u);
1509 using Vector3Pair = std::pair<Vector3,Vector3>;
1511 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1512 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1513 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1514 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1515 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1516 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1517 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1518 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1519 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1520 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)