Use a sorted vector for the decoders
[alure.git] / src / context.cpp
blobe94fb75540305d8e2158e82fe66c3a9f01ce1970
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(const String &name, UniquePtr<DecoderFactory> factory)
308 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
309 [](const DecoderEntryPair &entry, const String &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(name, std::move(factory)));
317 UniquePtr<DecoderFactory> UnregisterDecoder(const String &name)
319 auto iter = std::lower_bound(sDecoders.begin(), sDecoders.end(), name,
320 [](const DecoderEntryPair &entry, const String &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 RingBuffer::Data ringdata = mPendingBuffers.get_read_vector()[0];
541 if(ringdata.len > 0)
543 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata.buf);
544 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, this);
545 pb->mPromise.set_value(Buffer(pb->mBuffer));
546 pb->~PendingBuffer();
547 mPendingBuffers.read_advance(1);
548 continue;
551 std::unique_lock<std::mutex> wakelock(mWakeMutex);
552 if(!mQuitThread.load(std::memory_order_acquire) && mPendingBuffers.read_space() == 0)
554 ctxlock.unlock();
556 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
557 if(interval.count() == 0)
558 mWakeThread.wait(wakelock);
559 else
561 auto now = std::chrono::steady_clock::now() - basetime;
562 if(now > waketime)
564 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
565 waketime += interval * mult;
566 mWakeThread.wait_until(wakelock, waketime + basetime);
569 wakelock.unlock();
571 ctxlock.lock();
572 while(!mQuitThread.load(std::memory_order_acquire) &&
573 alcGetCurrentContext() != getContext())
574 mWakeThread.wait(ctxlock);
577 ctxlock.unlock();
579 if(DeviceManagerImpl::SetThreadContext)
580 DeviceManagerImpl::SetThreadContext(nullptr);
584 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
585 : mListener(this), mContext(context), mDevice(device),
586 mWakeInterval(std::chrono::milliseconds::zero()),
587 mPendingBuffers(16, sizeof(PendingBuffer)), 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)
602 ContextImpl::~ContextImpl()
604 auto ringdata = mPendingBuffers.get_read_vector();
605 if(ringdata[0].len > 0)
607 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
608 for(size_t i = 0;i < ringdata[0].len;i++)
609 pb[i].~PendingBuffer();
610 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
611 for(size_t i = 0;i < ringdata[1].len;i++)
612 pb[i].~PendingBuffer();
614 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
619 void ContextImpl::destroy()
621 if(mRefs.load() != 0)
622 throw std::runtime_error("Context is in use");
623 if(!mBuffers.empty())
624 throw std::runtime_error("Trying to destroy a context with buffers");
626 if(mThread.joinable())
628 std::unique_lock<std::mutex> lock(mWakeMutex);
629 mQuitThread.store(true, std::memory_order_release);
630 lock.unlock();
631 mWakeThread.notify_all();
632 mThread.join();
635 alcDestroyContext(mContext);
636 mContext = nullptr;
638 mDevice->removeContext(this);
642 void ContextImpl::startBatch()
644 alcSuspendContext(mContext);
645 mIsBatching = true;
648 void ContextImpl::endBatch()
650 alcProcessContext(mContext);
651 mIsBatching = false;
655 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler> handler)
657 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
658 mMessage.swap(handler);
659 return handler;
663 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
665 if(interval.count() < 0 || interval > std::chrono::seconds(1))
666 throw std::runtime_error("Async wake interval out of range");
667 mWakeInterval.store(interval);
668 mWakeMutex.lock(); mWakeMutex.unlock();
669 mWakeThread.notify_all();
673 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
675 DecoderOrExceptT ret;
677 String oldname = String(name);
678 auto file = FileIOFactory::get().openFile(oldname);
679 if(file) return (ret = GetDecoder(name, std::move(file)));
681 // Resource not found. Try to find a substitute.
682 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+oldname));
683 do {
684 String newname(mMessage->resourceNotFound(oldname));
685 if(newname.empty())
686 return (ret = std::runtime_error("Failed to open "+oldname));
687 file = FileIOFactory::get().openFile(newname);
688 oldname = std::move(newname);
689 } while(!file);
691 return (ret = GetDecoder(oldname, std::move(file)));
694 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
696 CheckContext(this);
697 DecoderOrExceptT dec = findDecoder(name);
698 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
699 return *decoder;
700 throw std::get<std::runtime_error>(dec);
704 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
706 CheckContext(this);
707 return GetFormat(channels, type) != AL_NONE;
711 ArrayView<String> ContextImpl::getAvailableResamplers()
713 CheckContext(this);
714 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
716 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
717 mResamplers.reserve(num_resamplers);
718 for(int i = 0;i < num_resamplers;i++)
719 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
720 if(mResamplers.empty())
721 mResamplers.emplace_back();
723 return mResamplers;
726 ALsizei ContextImpl::getDefaultResamplerIndex() const
728 CheckContext(this);
729 if(!hasExtension(SOFT_source_resampler))
730 return 0;
731 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
735 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
737 BufferOrExceptT retval;
738 ALuint srate = decoder->getFrequency();
739 ChannelConfig chans = decoder->getChannelConfig();
740 SampleType type = decoder->getSampleType();
741 ALuint frames = decoder->getLength();
743 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
744 frames = decoder->read(data.data(), frames);
745 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
746 data.resize(FramesToBytes(frames, chans, type));
748 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
749 if(loop_pts.first >= loop_pts.second)
750 loop_pts = std::make_pair(0, frames);
751 else
753 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
754 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
757 // Get the format before calling the bufferLoading message handler, to
758 // ensure it's something OpenAL can handle.
759 ALenum format = GetFormat(chans, type);
760 if(format == AL_NONE)
762 std::stringstream sstr;
763 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
764 return (retval = std::runtime_error(sstr.str()));
767 if(mMessage.get())
768 mMessage->bufferLoading(name, chans, type, srate, data);
770 alGetError();
771 ALuint bid = 0;
772 alGenBuffers(1, &bid);
773 alBufferData(bid, format, data.data(), data.size(), srate);
774 if(hasExtension(SOFT_loop_points))
776 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
777 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
779 if(alGetError() != AL_NO_ERROR)
781 alDeleteBuffers(1, &bid);
782 return (retval = std::runtime_error("Failed to buffer data"));
785 return (retval = mBuffers.insert(iter,
786 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
787 )->get());
790 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
792 BufferOrExceptT retval;
793 ALuint srate = decoder->getFrequency();
794 ChannelConfig chans = decoder->getChannelConfig();
795 SampleType type = decoder->getSampleType();
796 ALuint frames = decoder->getLength();
797 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
799 ALenum format = GetFormat(chans, type);
800 if(format == AL_NONE)
802 std::stringstream sstr;
803 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
804 return (retval = std::runtime_error(sstr.str()));
807 alGetError();
808 ALuint bid = 0;
809 alGenBuffers(1, &bid);
810 if(alGetError() != AL_NO_ERROR)
811 return (retval = std::runtime_error("Failed to create buffer"));
813 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
815 if(mThread.get_id() == std::thread::id())
816 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
818 while(mPendingBuffers.write_space() == 0)
820 mWakeThread.notify_all();
821 std::this_thread::yield();
824 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
825 new(ringdata.buf) PendingBuffer{buffer.get(), decoder, format, frames, std::move(promise)};
826 mPendingBuffers.write_advance(1);
828 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
831 Buffer ContextImpl::getBuffer(StringView name)
833 CheckContext(this);
835 auto hasher = std::hash<StringView>();
836 if(EXPECT(!mFutureBuffers.empty(), false))
838 Buffer buffer;
840 // If the buffer is already pending for the future, wait for it
841 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
842 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
843 { return hasher(lhs.mBuffer->getName()) < rhs; }
845 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
847 buffer = iter->mFuture.get();
848 mFutureBuffers.erase(iter);
851 // Clear out any completed futures.
852 mFutureBuffers.erase(
853 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
854 [](const PendingFuture &entry) -> bool
855 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
856 ), mFutureBuffers.end()
859 // If we got the buffer, return it. Otherwise, go load it normally.
860 if(buffer) return buffer;
863 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
864 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
865 { return hasher(lhs->getName()) < rhs; }
867 if(iter != mBuffers.end() && (*iter)->getName() == name)
868 return Buffer(iter->get());
870 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
871 Buffer *buffer = std::get_if<Buffer>(&ret);
872 if(EXPECT(!buffer, false))
873 throw std::get<std::runtime_error>(ret);
874 return *buffer;
877 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
879 SharedFuture<Buffer> future;
880 CheckContext(this);
882 auto hasher = std::hash<StringView>();
883 if(EXPECT(!mFutureBuffers.empty(), false))
885 // Check if the future that's being created already exists
886 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
887 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
888 { return hasher(lhs.mBuffer->getName()) < rhs; }
890 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
892 future = iter->mFuture;
893 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
894 mFutureBuffers.erase(iter);
895 return future;
898 // Clear out any fulfilled futures.
899 mFutureBuffers.erase(
900 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
901 [](const PendingFuture &entry) -> bool
902 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
903 ), mFutureBuffers.end()
907 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
908 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
909 { return hasher(lhs->getName()) < rhs; }
911 if(iter != mBuffers.end() && (*iter)->getName() == name)
913 // User asked to create a future buffer that's already loaded. Just
914 // construct a promise, fulfill the promise immediately, then return a
915 // shared future that's already set.
916 Promise<Buffer> promise;
917 promise.set_value(Buffer(iter->get()));
918 return promise.get_future().share();
921 Promise<Buffer> promise;
922 future = promise.get_future().share();
924 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
925 Buffer *buffer = std::get_if<Buffer>(&ret);
926 if(EXPECT(!buffer, false))
927 throw std::get<std::runtime_error>(ret);
928 mWakeMutex.lock(); mWakeMutex.unlock();
929 mWakeThread.notify_all();
931 mFutureBuffers.insert(
932 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
933 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
934 { return hasher(lhs.mBuffer->getName()) < rhs; }
935 ), { buffer->getHandle(), future }
938 return future;
941 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
943 CheckContext(this);
945 if(EXPECT(!mFutureBuffers.empty(), false))
947 // Clear out any fulfilled futures.
948 mFutureBuffers.erase(
949 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
950 [](const PendingFuture &entry) -> bool
951 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
952 ), mFutureBuffers.end()
956 auto hasher = std::hash<StringView>();
957 for(const StringView name : names)
959 // Check if the buffer that's being created already exists
960 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
961 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
962 { return hasher(lhs->getName()) < rhs; }
964 if(iter != mBuffers.end() && (*iter)->getName() == name)
965 continue;
967 DecoderOrExceptT dec = findDecoder(name);
968 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
969 if(!decoder) continue;
971 Promise<Buffer> promise;
972 SharedFuture<Buffer> future = promise.get_future().share();
974 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
975 std::move(promise));
976 Buffer *buffer = std::get_if<Buffer>(&buf);
977 if(EXPECT(!buffer, false)) continue;
979 mFutureBuffers.insert(
980 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
981 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
982 { return hasher(lhs.mBuffer->getName()) < rhs; }
983 ), { buffer->getHandle(), future }
986 mWakeMutex.lock(); mWakeMutex.unlock();
987 mWakeThread.notify_all();
990 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder> decoder)
992 CheckContext(this);
994 auto hasher = std::hash<StringView>();
995 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
996 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
997 { return hasher(lhs->getName()) < rhs; }
999 if(iter != mBuffers.end() && (*iter)->getName() == name)
1000 throw std::runtime_error("Buffer \""+name+"\" already exists");
1002 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
1003 Buffer *buffer = std::get_if<Buffer>(&ret);
1004 if(EXPECT(!buffer, false))
1005 throw std::get<std::runtime_error>(ret);
1006 return *buffer;
1009 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder> decoder)
1011 SharedFuture<Buffer> future;
1012 CheckContext(this);
1014 if(EXPECT(!mFutureBuffers.empty(), false))
1016 // Clear out any fulfilled futures.
1017 mFutureBuffers.erase(
1018 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1019 [](const PendingFuture &entry) -> bool
1020 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1021 ), mFutureBuffers.end()
1025 auto hasher = std::hash<StringView>();
1026 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1027 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1028 { return hasher(lhs->getName()) < rhs; }
1030 if(iter != mBuffers.end() && (*iter)->getName() == name)
1031 throw std::runtime_error("Buffer \""+name+"\" already exists");
1033 Promise<Buffer> promise;
1034 future = promise.get_future().share();
1036 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1037 Buffer *buffer = std::get_if<Buffer>(&ret);
1038 if(EXPECT(!buffer, false))
1039 throw std::get<std::runtime_error>(ret);
1040 mWakeMutex.lock(); mWakeMutex.unlock();
1041 mWakeThread.notify_all();
1043 mFutureBuffers.insert(
1044 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1045 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1046 { return hasher(lhs.mBuffer->getName()) < rhs; }
1047 ), { buffer->getHandle(), future }
1050 return future;
1054 void ContextImpl::removeBuffer(StringView name)
1056 CheckContext(this);
1058 auto hasher = std::hash<StringView>();
1059 if(EXPECT(!mFutureBuffers.empty(), false))
1061 // If the buffer is already pending for the future, wait for it to
1062 // finish before continuing.
1063 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1064 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1065 { return hasher(lhs.mBuffer->getName()) < rhs; }
1067 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1069 iter->mFuture.wait();
1070 mFutureBuffers.erase(iter);
1073 // Clear out any completed futures.
1074 mFutureBuffers.erase(
1075 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1076 [](const PendingFuture &entry) -> bool
1077 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1078 ), mFutureBuffers.end()
1082 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1083 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1084 { return hasher(lhs->getName()) < rhs; }
1086 if(iter != mBuffers.end() && (*iter)->getName() == name)
1088 (*iter)->cleanup();
1089 mBuffers.erase(iter);
1094 ALuint ContextImpl::getSourceId(ALuint maxprio)
1096 ALuint id = 0;
1097 if(mSourceIds.empty())
1099 alGetError();
1100 alGenSources(1, &id);
1101 if(alGetError() == AL_NO_ERROR)
1102 return id;
1104 SourceImpl *lowest = nullptr;
1105 for(SourceBufferUpdateEntry &entry : mPlaySources)
1107 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1108 lowest = entry.mSource;
1110 for(SourceStreamUpdateEntry &entry : mStreamSources)
1112 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1113 lowest = entry.mSource;
1115 if(lowest && lowest->getPriority() < maxprio)
1117 lowest->stop();
1118 if(mMessage.get())
1119 mMessage->sourceForceStopped(lowest);
1122 if(mSourceIds.empty())
1123 throw std::runtime_error("No available sources");
1125 id = mSourceIds.top();
1126 mSourceIds.pop();
1127 return id;
1131 Source ContextImpl::createSource()
1133 CheckContext(this);
1135 SourceImpl *source;
1136 if(!mFreeSources.empty())
1138 source = mFreeSources.front();
1139 mFreeSources.pop();
1141 else
1143 mAllSources.emplace_back(this);
1144 source = &mAllSources.back();
1146 return Source(source);
1150 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1152 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1153 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1154 { return lhs.mSource < rhs; }
1156 if(iter == mPlaySources.end() || iter->mSource != source)
1157 mPlaySources.insert(iter, {source,id});
1160 void ContextImpl::addPlayingSource(SourceImpl *source)
1162 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1163 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1164 { return lhs.mSource < rhs; }
1166 if(iter == mStreamSources.end() || iter->mSource != source)
1167 mStreamSources.insert(iter, {source});
1170 void ContextImpl::removePlayingSource(SourceImpl *source)
1172 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1173 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1174 { return lhs.mSource < rhs; }
1176 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1177 mPlaySources.erase(iter0);
1178 else
1180 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1181 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1182 { return lhs.mSource < rhs; }
1184 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1185 mStreamSources.erase(iter1);
1190 void ContextImpl::addStream(SourceImpl *source)
1192 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1193 if(mThread.get_id() == std::thread::id())
1194 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1195 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1196 if(iter == mStreamingSources.end() || *iter != source)
1197 mStreamingSources.insert(iter, source);
1200 void ContextImpl::removeStream(SourceImpl *source)
1202 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1203 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1204 if(iter != mStreamingSources.end() && *iter == source)
1205 mStreamingSources.erase(iter);
1208 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1210 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1211 if(iter != mStreamingSources.end() && *iter == source)
1212 mStreamingSources.erase(iter);
1216 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1218 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
1219 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1220 CheckContext(this);
1222 alGetError();
1223 ALuint id = 0;
1224 alGenAuxiliaryEffectSlots(1, &id);
1225 if(alGetError() != AL_NO_ERROR)
1226 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1227 try {
1228 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1230 catch(...) {
1231 alDeleteAuxiliaryEffectSlots(1, &id);
1232 throw;
1237 Effect ContextImpl::createEffect()
1239 if(!hasExtension(EXT_EFX))
1240 throw std::runtime_error("Effects not supported");
1241 CheckContext(this);
1243 alGetError();
1244 ALuint id = 0;
1245 alGenEffects(1, &id);
1246 if(alGetError() != AL_NO_ERROR)
1247 throw std::runtime_error("Failed to create Effect");
1248 try {
1249 return Effect(new EffectImpl(this, id));
1251 catch(...) {
1252 alDeleteEffects(1, &id);
1253 throw;
1258 SourceGroup ContextImpl::createSourceGroup(StringView name)
1260 auto hasher = std::hash<StringView>();
1261 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1262 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1263 { return hasher(lhs->getName()) < rhs; }
1265 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1266 throw std::runtime_error("Duplicate source group name");
1267 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, String(name)));
1268 return SourceGroup(iter->get());
1271 SourceGroup ContextImpl::getSourceGroup(StringView name)
1273 auto hasher = std::hash<StringView>();
1274 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), hasher(name),
1275 [hasher](const UniquePtr<SourceGroupImpl> &lhs, size_t rhs) -> bool
1276 { return hasher(lhs->getName()) < rhs; }
1278 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1279 throw std::runtime_error("Source group not found");
1280 return SourceGroup(iter->get());
1283 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1285 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1286 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1287 { return lhs->getName() < rhs; }
1289 if(iter != mSourceGroups.end() && iter->get() == group)
1290 mSourceGroups.erase(iter);
1294 void ContextImpl::setDopplerFactor(ALfloat factor)
1296 if(!(factor >= 0.0f))
1297 throw std::runtime_error("Doppler factor out of range");
1298 CheckContext(this);
1299 alDopplerFactor(factor);
1303 void ContextImpl::setSpeedOfSound(ALfloat speed)
1305 if(!(speed > 0.0f))
1306 throw std::runtime_error("Speed of sound out of range");
1307 CheckContext(this);
1308 alSpeedOfSound(speed);
1312 void ContextImpl::setDistanceModel(DistanceModel model)
1314 CheckContext(this);
1315 alDistanceModel((ALenum)model);
1319 void ContextImpl::update()
1321 CheckContext(this);
1322 mPlaySources.erase(
1323 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1324 [](const SourceBufferUpdateEntry &entry) -> bool
1325 { return !entry.mSource->playUpdate(entry.mId); }
1326 ), mPlaySources.end()
1328 mStreamSources.erase(
1329 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1330 [](const SourceStreamUpdateEntry &entry) -> bool
1331 { return !entry.mSource->playUpdate(); }
1332 ), mStreamSources.end()
1334 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1336 // For performance reasons, don't wait for the thread's mutex. This
1337 // should be called often enough to keep up with any and all streams
1338 // regardless.
1339 mWakeThread.notify_all();
1342 if(hasExtension(EXT_disconnect) && mIsConnected)
1344 ALCint connected;
1345 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1346 mIsConnected = connected;
1347 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1351 void Context::destroy()
1353 pImpl->destroy();
1354 pImpl = nullptr;
1356 DECL_THUNK0(Device, Context, getDevice,)
1357 DECL_THUNK0(void, Context, startBatch,)
1358 DECL_THUNK0(void, Context, endBatch,)
1359 DECL_THUNK0(Listener, Context, getListener,)
1360 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1361 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1362 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1363 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1364 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
1365 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1366 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1367 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1368 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1369 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1370 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1371 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1372 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1373 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1374 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1375 DECL_THUNK0(Source, Context, createSource,)
1376 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1377 DECL_THUNK0(Effect, Context, createEffect,)
1378 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, StringView)
1379 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, StringView)
1380 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1381 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1382 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1383 DECL_THUNK0(void, Context, update,)
1386 void Context::MakeCurrent(Context context)
1387 { ContextImpl::MakeCurrent(context.pImpl); }
1389 Context Context::GetCurrent()
1390 { return Context(ContextImpl::GetCurrent()); }
1392 void Context::MakeThreadCurrent(Context context)
1393 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1395 Context Context::GetThreadCurrent()
1396 { return Context(ContextImpl::GetThreadCurrent()); }
1399 void ListenerImpl::setGain(ALfloat gain)
1401 if(!(gain >= 0.0f))
1402 throw std::runtime_error("Gain out of range");
1403 CheckContext(mContext);
1404 alListenerf(AL_GAIN, gain);
1408 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1410 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1411 CheckContext(mContext);
1412 Batcher batcher = mContext->getBatcher();
1413 alListenerfv(AL_POSITION, position.getPtr());
1414 alListenerfv(AL_VELOCITY, velocity.getPtr());
1415 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1418 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1420 CheckContext(mContext);
1421 alListener3f(AL_POSITION, x, y, z);
1424 void ListenerImpl::setPosition(const ALfloat *pos)
1426 CheckContext(mContext);
1427 alListenerfv(AL_POSITION, pos);
1430 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1432 CheckContext(mContext);
1433 alListener3f(AL_VELOCITY, x, y, z);
1436 void ListenerImpl::setVelocity(const ALfloat *vel)
1438 CheckContext(mContext);
1439 alListenerfv(AL_VELOCITY, vel);
1442 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1444 CheckContext(mContext);
1445 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1446 alListenerfv(AL_ORIENTATION, ori);
1449 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1451 CheckContext(mContext);
1452 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1453 alListenerfv(AL_ORIENTATION, ori);
1456 void ListenerImpl::setOrientation(const ALfloat *ori)
1458 CheckContext(mContext);
1459 alListenerfv(AL_ORIENTATION, ori);
1462 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1464 if(!(m_u > 0.0f))
1465 throw std::runtime_error("Invalid meters per unit");
1466 CheckContext(mContext);
1467 if(mContext->hasExtension(EXT_EFX))
1468 alListenerf(AL_METERS_PER_UNIT, m_u);
1472 using Vector3Pair = std::pair<Vector3,Vector3>;
1474 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1475 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1476 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1477 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1478 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1479 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1480 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1481 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1482 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1483 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)