Use remove_if to clean up loaded future buffers
[alure.git] / src / context.cpp
blob9ef04e60fd8af0c8155c5d55f383ced69b89326a
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<traits_type::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 const std::pair<alure::String,alure::UniquePtr<alure::DecoderFactory>> sDefaultDecoders[] = {
229 { "_alure_int_wave", alure::MakeUnique<alure::WaveDecoderFactory>() },
231 #ifdef HAVE_VORBISFILE
232 { "_alure_int_vorbis", alure::MakeUnique<alure::VorbisFileDecoderFactory>() },
233 #endif
234 #ifdef HAVE_LIBFLAC
235 { "_alure_int_flac", alure::MakeUnique<alure::FlacDecoderFactory>() },
236 #endif
237 #ifdef HAVE_OPUSFILE
238 { "_alure_int_opus", alure::MakeUnique<alure::OpusFileDecoderFactory>() },
239 #endif
240 #ifdef HAVE_LIBSNDFILE
241 { "_alure_int_sndfile", alure::MakeUnique<alure::SndFileDecoderFactory>() },
242 #endif
243 #ifdef HAVE_MPG123
244 { "_alure_int_mpg123", alure::MakeUnique<alure::Mpg123DecoderFactory>() },
245 #endif
247 std::map<alure::String,alure::UniquePtr<alure::DecoderFactory>> sDecoders;
250 template<typename T>
251 alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> &file,
252 T start, T end)
254 alure::DecoderOrExceptT ret;
255 while(start != end)
257 alure::DecoderFactory *factory = start->second.get();
258 auto decoder = factory->createDecoder(file);
259 if(decoder) return (ret = std::move(decoder));
261 if(!file || !(file->clear(),file->seekg(0)))
262 return (ret = std::runtime_error("Failed to rewind "+name+" for the next decoder factory"));
264 ++start;
267 return (ret = nullptr);
270 static alure::DecoderOrExceptT GetDecoder(alure::StringView name, alure::UniquePtr<std::istream> file)
272 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
273 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
274 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
275 decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
276 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
277 if(std::get<alure::SharedPtr<alure::Decoder>>(decoder)) return decoder;
278 return (decoder = std::runtime_error("No decoder for "+name));
281 class DefaultFileIOFactory final : public alure::FileIOFactory {
282 alure::UniquePtr<std::istream> openFile(const alure::String &name) override
284 #ifdef _WIN32
285 auto file = alure::MakeUnique<Stream>(name.c_str());
286 #else
287 auto file = alure::MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
288 #endif
289 if(!file->is_open()) file = nullptr;
290 return std::move(file);
293 DefaultFileIOFactory sDefaultFileFactory;
295 alure::UniquePtr<alure::FileIOFactory> sFileFactory;
299 namespace alure
302 Decoder::~Decoder() { }
303 DecoderFactory::~DecoderFactory() { }
305 void RegisterDecoder(const String &name, UniquePtr<DecoderFactory> factory)
307 while(sDecoders.find(name) != sDecoders.end())
308 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
309 sDecoders.insert(std::make_pair(name, std::move(factory)));
312 UniquePtr<DecoderFactory> UnregisterDecoder(const String &name)
314 auto iter = sDecoders.find(name);
315 if(iter != sDecoders.end())
317 UniquePtr<DecoderFactory> factory = std::move(iter->second);
318 sDecoders.erase(iter);
319 return factory;
321 return nullptr;
325 FileIOFactory::~FileIOFactory() { }
327 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
329 std::swap(sFileFactory, factory);
330 return factory;
333 FileIOFactory &FileIOFactory::get()
335 FileIOFactory *factory = sFileFactory.get();
336 if(factory) return *factory;
337 return sDefaultFileFactory;
341 // Default message handler methods are no-ops.
342 MessageHandler::~MessageHandler()
346 void MessageHandler::deviceDisconnected(Device)
350 void MessageHandler::sourceStopped(Source)
354 void MessageHandler::sourceForceStopped(Source)
358 void MessageHandler::bufferLoading(StringView, ChannelConfig, SampleType, ALuint, ArrayView<ALbyte>)
362 String MessageHandler::resourceNotFound(StringView)
364 return String();
368 template<typename T>
369 static inline void LoadALFunc(T **func, const char *name)
370 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
372 static void LoadNothing(ContextImpl*) { }
374 static void LoadEFX(ContextImpl *ctx)
376 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
377 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
378 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
379 LoadALFunc(&ctx->alEffecti, "alEffecti");
380 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
381 LoadALFunc(&ctx->alEffectf, "alEffectf");
382 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
383 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
384 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
385 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
386 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
388 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
389 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
390 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
391 LoadALFunc(&ctx->alFilteri, "alFilteri");
392 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
393 LoadALFunc(&ctx->alFilterf, "alFilterf");
394 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
395 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
396 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
397 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
398 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
400 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
401 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
402 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
403 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
404 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
405 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
406 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
407 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
408 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
409 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
410 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
413 static void LoadSourceResampler(ContextImpl *ctx)
415 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
418 static void LoadSourceLatency(ContextImpl *ctx)
420 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
421 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
424 static const struct {
425 enum ALExtension extension;
426 const char name[32];
427 void (&loader)(ContextImpl*);
428 } ALExtensionList[] = {
429 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
431 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
432 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
433 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
435 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
436 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
437 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
439 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
440 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
441 { SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
442 { SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
444 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
446 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
447 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
451 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
452 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
454 void ContextImpl::MakeCurrent(ContextImpl *context)
456 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
458 if(alcMakeContextCurrent(context ? context->getContext() : nullptr) == ALC_FALSE)
459 throw std::runtime_error("Call to alcMakeContextCurrent failed");
460 if(context)
462 context->addRef();
463 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
465 std::swap(sCurrentCtx, context);
466 if(context) context->decRef();
468 if(sThreadCurrentCtx)
469 sThreadCurrentCtx->decRef();
470 sThreadCurrentCtx = 0;
472 if((context = sCurrentCtx) != nullptr)
474 ctxlock.unlock();
475 context->mWakeThread.notify_all();
479 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
481 if(!DeviceManagerImpl::SetThreadContext)
482 throw std::runtime_error("Thread-local contexts unsupported");
483 if(DeviceManagerImpl::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
484 throw std::runtime_error("Call to alcSetThreadContext failed");
485 if(context)
487 context->addRef();
488 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
490 if(sThreadCurrentCtx)
491 sThreadCurrentCtx->decRef();
492 sThreadCurrentCtx = context;
495 void ContextImpl::setupExts()
497 ALCdevice *device = mDevice->getDevice();
498 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
499 for(const auto &entry : ALExtensionList)
501 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
502 alcIsExtensionPresent(device, entry.name) :
503 alIsExtensionPresent(entry.name);
504 if(mHasExt[entry.extension]) entry.loader(this);
509 void ContextImpl::backgroundProc()
511 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
512 DeviceManagerImpl::SetThreadContext(getContext());
514 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
515 std::chrono::milliseconds waketime(0);
516 std::unique_lock<std::mutex> ctxlock(mGlobalCtxMutex);
517 while(!mQuitThread.load(std::memory_order_acquire))
520 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
521 mStreamingSources.erase(
522 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
523 [](SourceImpl *source) -> bool
524 { return !source->updateAsync(); }
525 ), mStreamingSources.end()
529 // Only do one pending buffer at a time. In case there's several large
530 // buffers to load, we still need to process streaming sources so they
531 // don't underrun.
532 RingBuffer::Data ringdata = mPendingBuffers.get_read_vector()[0];
533 if(ringdata.len > 0)
535 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata.buf);
536 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, this);
537 pb->mPromise.set_value(Buffer(pb->mBuffer));
538 pb->~PendingBuffer();
539 mPendingBuffers.read_advance(1);
540 continue;
543 std::unique_lock<std::mutex> wakelock(mWakeMutex);
544 if(!mQuitThread.load(std::memory_order_acquire) && mPendingBuffers.read_space() == 0)
546 ctxlock.unlock();
548 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
549 if(interval.count() == 0)
550 mWakeThread.wait(wakelock);
551 else
553 auto now = std::chrono::steady_clock::now() - basetime;
554 if(now > waketime)
556 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
557 waketime += interval * mult;
558 mWakeThread.wait_until(wakelock, waketime + basetime);
561 wakelock.unlock();
563 ctxlock.lock();
564 while(!mQuitThread.load(std::memory_order_acquire) &&
565 alcGetCurrentContext() != getContext())
566 mWakeThread.wait(ctxlock);
569 ctxlock.unlock();
571 if(DeviceManagerImpl::SetThreadContext)
572 DeviceManagerImpl::SetThreadContext(nullptr);
576 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
577 : mListener(this), mContext(context), mDevice(device),
578 mWakeInterval(std::chrono::milliseconds::zero()),
579 mPendingBuffers(16, sizeof(PendingBuffer)), mQuitThread(false),
580 mRefs(0), mHasExt{false}, mIsConnected(true), mIsBatching(false),
581 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
582 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
583 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
584 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
585 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
586 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
587 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
588 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
589 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
590 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
594 ContextImpl::~ContextImpl()
596 auto ringdata = mPendingBuffers.get_read_vector();
597 if(ringdata[0].len > 0)
599 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
600 for(size_t i = 0;i < ringdata[0].len;i++)
601 pb[i].~PendingBuffer();
602 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
603 for(size_t i = 0;i < ringdata[1].len;i++)
604 pb[i].~PendingBuffer();
606 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
611 void ContextImpl::destroy()
613 if(mRefs.load() != 0)
614 throw std::runtime_error("Context is in use");
615 if(!mBuffers.empty())
616 throw std::runtime_error("Trying to destroy a context with buffers");
618 if(mThread.joinable())
620 std::unique_lock<std::mutex> lock(mWakeMutex);
621 mQuitThread.store(true, std::memory_order_release);
622 lock.unlock();
623 mWakeThread.notify_all();
624 mThread.join();
627 alcDestroyContext(mContext);
628 mContext = nullptr;
630 mDevice->removeContext(this);
634 void ContextImpl::startBatch()
636 alcSuspendContext(mContext);
637 mIsBatching = true;
640 void ContextImpl::endBatch()
642 alcProcessContext(mContext);
643 mIsBatching = false;
647 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler> handler)
649 std::lock_guard<std::mutex> lock(mGlobalCtxMutex);
650 mMessage.swap(handler);
651 return handler;
655 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval)
657 if(interval.count() < 0 || interval > std::chrono::seconds(1))
658 throw std::runtime_error("Async wake interval out of range");
659 mWakeInterval.store(interval);
660 mWakeMutex.lock(); mWakeMutex.unlock();
661 mWakeThread.notify_all();
665 DecoderOrExceptT ContextImpl::findDecoder(StringView name)
667 DecoderOrExceptT ret;
669 String oldname = String(name);
670 auto file = FileIOFactory::get().openFile(oldname);
671 if(file) return (ret = GetDecoder(name, std::move(file)));
673 // Resource not found. Try to find a substitute.
674 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+oldname));
675 do {
676 String newname(mMessage->resourceNotFound(oldname));
677 if(newname.empty())
678 return (ret = std::runtime_error("Failed to open "+oldname));
679 file = FileIOFactory::get().openFile(newname);
680 oldname = std::move(newname);
681 } while(!file);
683 return (ret = GetDecoder(oldname, std::move(file)));
686 SharedPtr<Decoder> ContextImpl::createDecoder(StringView name)
688 CheckContext(this);
689 DecoderOrExceptT dec = findDecoder(name);
690 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
691 return *decoder;
692 throw std::get<std::runtime_error>(dec);
696 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
698 CheckContext(this);
699 return GetFormat(channels, type) != AL_NONE;
703 ArrayView<String> ContextImpl::getAvailableResamplers()
705 CheckContext(this);
706 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
708 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
709 mResamplers.reserve(num_resamplers);
710 for(int i = 0;i < num_resamplers;i++)
711 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
712 if(mResamplers.empty())
713 mResamplers.emplace_back();
715 return mResamplers;
718 ALsizei ContextImpl::getDefaultResamplerIndex() const
720 CheckContext(this);
721 if(!hasExtension(SOFT_source_resampler))
722 return 0;
723 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
727 BufferOrExceptT ContextImpl::doCreateBuffer(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
729 BufferOrExceptT retval;
730 ALuint srate = decoder->getFrequency();
731 ChannelConfig chans = decoder->getChannelConfig();
732 SampleType type = decoder->getSampleType();
733 ALuint frames = decoder->getLength();
735 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
736 frames = decoder->read(data.data(), frames);
737 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
738 data.resize(FramesToBytes(frames, chans, type));
740 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
741 if(loop_pts.first >= loop_pts.second)
742 loop_pts = std::make_pair(0, frames);
743 else
745 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
746 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
749 // Get the format before calling the bufferLoading message handler, to
750 // ensure it's something OpenAL can handle.
751 ALenum format = GetFormat(chans, type);
752 if(format == AL_NONE)
754 std::stringstream sstr;
755 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
756 return (retval = std::runtime_error(sstr.str()));
759 if(mMessage.get())
760 mMessage->bufferLoading(name, chans, type, srate, data);
762 alGetError();
763 ALuint bid = 0;
764 alGenBuffers(1, &bid);
765 alBufferData(bid, format, data.data(), data.size(), srate);
766 if(hasExtension(SOFT_loop_points))
768 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
769 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
771 if(alGetError() != AL_NO_ERROR)
773 alDeleteBuffers(1, &bid);
774 return (retval = std::runtime_error("Failed to buffer data"));
777 return (retval = mBuffers.insert(iter,
778 MakeUnique<BufferImpl>(this, bid, srate, chans, type, name)
779 )->get());
782 BufferOrExceptT ContextImpl::doCreateBufferAsync(StringView name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder, Promise<Buffer> promise)
784 BufferOrExceptT retval;
785 ALuint srate = decoder->getFrequency();
786 ChannelConfig chans = decoder->getChannelConfig();
787 SampleType type = decoder->getSampleType();
788 ALuint frames = decoder->getLength();
789 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
791 ALenum format = GetFormat(chans, type);
792 if(format == AL_NONE)
794 std::stringstream sstr;
795 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
796 return (retval = std::runtime_error(sstr.str()));
799 alGetError();
800 ALuint bid = 0;
801 alGenBuffers(1, &bid);
802 if(alGetError() != AL_NO_ERROR)
803 return (retval = std::runtime_error("Failed to create buffer"));
805 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, name);
807 if(mThread.get_id() == std::thread::id())
808 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
810 while(mPendingBuffers.write_space() == 0)
812 mWakeThread.notify_all();
813 std::this_thread::yield();
816 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
817 new(ringdata.buf) PendingBuffer{buffer.get(), decoder, format, frames, std::move(promise)};
818 mPendingBuffers.write_advance(1);
820 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
823 Buffer ContextImpl::getBuffer(StringView name)
825 CheckContext(this);
827 auto hasher = std::hash<StringView>();
828 if(EXPECT(!mFutureBuffers.empty(), false))
830 Buffer buffer;
832 // If the buffer is already pending for the future, wait for it
833 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
834 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
835 { return hasher(lhs.mBuffer->getName()) < rhs; }
837 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
839 buffer = iter->mFuture.get();
840 mFutureBuffers.erase(iter);
843 // Clear out any completed futures.
844 mFutureBuffers.erase(
845 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
846 [](const PendingFuture &entry) -> bool
847 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
848 ), mFutureBuffers.end()
851 // If we got the buffer, return it. Otherwise, go load it normally.
852 if(buffer) return buffer;
855 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
856 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
857 { return hasher(lhs->getName()) < rhs; }
859 if(iter != mBuffers.end() && (*iter)->getName() == name)
860 return Buffer(iter->get());
862 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
863 Buffer *buffer = std::get_if<Buffer>(&ret);
864 if(EXPECT(!buffer, false))
865 throw std::get<std::runtime_error>(ret);
866 return *buffer;
869 SharedFuture<Buffer> ContextImpl::getBufferAsync(StringView name)
871 SharedFuture<Buffer> future;
872 CheckContext(this);
874 auto hasher = std::hash<StringView>();
875 if(EXPECT(!mFutureBuffers.empty(), false))
877 // Check if the future that's being created already exists
878 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
879 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
880 { return hasher(lhs.mBuffer->getName()) < rhs; }
882 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
884 future = iter->mFuture;
885 if(future.wait_for(std::chrono::milliseconds::zero()) == std::future_status::ready)
886 mFutureBuffers.erase(iter);
887 return future;
890 // Clear out any fulfilled futures.
891 mFutureBuffers.erase(
892 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
893 [](const PendingFuture &entry) -> bool
894 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
895 ), mFutureBuffers.end()
899 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
900 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
901 { return hasher(lhs->getName()) < rhs; }
903 if(iter != mBuffers.end() && (*iter)->getName() == name)
905 // User asked to create a future buffer that's already loaded. Just
906 // construct a promise, fulfill the promise immediately, then return a
907 // shared future that's already set.
908 Promise<Buffer> promise;
909 promise.set_value(Buffer(iter->get()));
910 return promise.get_future().share();
913 Promise<Buffer> promise;
914 future = promise.get_future().share();
916 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name), std::move(promise));
917 Buffer *buffer = std::get_if<Buffer>(&ret);
918 if(EXPECT(!buffer, false))
919 throw std::get<std::runtime_error>(ret);
920 mWakeMutex.lock(); mWakeMutex.unlock();
921 mWakeThread.notify_all();
923 mFutureBuffers.insert(
924 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
925 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
926 { return hasher(lhs.mBuffer->getName()) < rhs; }
927 ), { buffer->getHandle(), future }
930 return future;
933 void ContextImpl::precacheBuffersAsync(ArrayView<StringView> names)
935 CheckContext(this);
937 if(EXPECT(!mFutureBuffers.empty(), false))
939 // Clear out any fulfilled futures.
940 mFutureBuffers.erase(
941 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
942 [](const PendingFuture &entry) -> bool
943 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
944 ), mFutureBuffers.end()
948 auto hasher = std::hash<StringView>();
949 for(const StringView name : names)
951 // Check if the buffer that's being created already exists
952 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
953 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
954 { return hasher(lhs->getName()) < rhs; }
956 if(iter != mBuffers.end() && (*iter)->getName() == name)
957 continue;
959 DecoderOrExceptT dec = findDecoder(name);
960 SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec);
961 if(!decoder) continue;
963 Promise<Buffer> promise;
964 SharedFuture<Buffer> future = promise.get_future().share();
966 BufferOrExceptT buf = doCreateBufferAsync(name, iter, std::move(*decoder),
967 std::move(promise));
968 Buffer *buffer = std::get_if<Buffer>(&buf);
969 if(EXPECT(!buffer, false)) continue;
971 mFutureBuffers.insert(
972 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
973 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
974 { return hasher(lhs.mBuffer->getName()) < rhs; }
975 ), { buffer->getHandle(), future }
978 mWakeMutex.lock(); mWakeMutex.unlock();
979 mWakeThread.notify_all();
982 Buffer ContextImpl::createBufferFrom(StringView name, SharedPtr<Decoder> decoder)
984 CheckContext(this);
986 auto hasher = std::hash<StringView>();
987 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
988 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
989 { return hasher(lhs->getName()) < rhs; }
991 if(iter != mBuffers.end() && (*iter)->getName() == name)
992 throw std::runtime_error("Buffer \""+name+"\" already exists");
994 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
995 Buffer *buffer = std::get_if<Buffer>(&ret);
996 if(EXPECT(!buffer, false))
997 throw std::get<std::runtime_error>(ret);
998 return *buffer;
1001 SharedFuture<Buffer> ContextImpl::createBufferAsyncFrom(StringView name, SharedPtr<Decoder> decoder)
1003 SharedFuture<Buffer> future;
1004 CheckContext(this);
1006 if(EXPECT(!mFutureBuffers.empty(), false))
1008 // Clear out any fulfilled futures.
1009 mFutureBuffers.erase(
1010 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1011 [](const PendingFuture &entry) -> bool
1012 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1013 ), mFutureBuffers.end()
1017 auto hasher = std::hash<StringView>();
1018 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1019 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1020 { return hasher(lhs->getName()) < rhs; }
1022 if(iter != mBuffers.end() && (*iter)->getName() == name)
1023 throw std::runtime_error("Buffer \""+name+"\" already exists");
1025 Promise<Buffer> promise;
1026 future = promise.get_future().share();
1028 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder), std::move(promise));
1029 Buffer *buffer = std::get_if<Buffer>(&ret);
1030 if(EXPECT(!buffer, false))
1031 throw std::get<std::runtime_error>(ret);
1032 mWakeMutex.lock(); mWakeMutex.unlock();
1033 mWakeThread.notify_all();
1035 mFutureBuffers.insert(
1036 std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1037 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1038 { return hasher(lhs.mBuffer->getName()) < rhs; }
1039 ), { buffer->getHandle(), future }
1042 return future;
1046 void ContextImpl::removeBuffer(StringView name)
1048 CheckContext(this);
1050 auto hasher = std::hash<StringView>();
1051 if(EXPECT(!mFutureBuffers.empty(), false))
1053 // If the buffer is already pending for the future, wait for it to
1054 // finish before continuing.
1055 auto iter = std::lower_bound(mFutureBuffers.begin(), mFutureBuffers.end(), hasher(name),
1056 [hasher](const PendingFuture &lhs, size_t rhs) -> bool
1057 { return hasher(lhs.mBuffer->getName()) < rhs; }
1059 if(iter != mFutureBuffers.end() && iter->mBuffer->getName() == name)
1061 iter->mFuture.wait();
1062 mFutureBuffers.erase(iter);
1065 // Clear out any completed futures.
1066 mFutureBuffers.erase(
1067 std::remove_if(mFutureBuffers.begin(), mFutureBuffers.end(),
1068 [](const PendingFuture &entry) -> bool
1069 { return entry.mFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready; }
1070 ), mFutureBuffers.end()
1074 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
1075 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
1076 { return hasher(lhs->getName()) < rhs; }
1078 if(iter != mBuffers.end() && (*iter)->getName() == name)
1080 (*iter)->cleanup();
1081 mBuffers.erase(iter);
1086 ALuint ContextImpl::getSourceId(ALuint maxprio)
1088 ALuint id = 0;
1089 if(mSourceIds.empty())
1091 alGetError();
1092 alGenSources(1, &id);
1093 if(alGetError() == AL_NO_ERROR)
1094 return id;
1096 SourceImpl *lowest = nullptr;
1097 for(SourceBufferUpdateEntry &entry : mPlaySources)
1099 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1100 lowest = entry.mSource;
1102 for(SourceStreamUpdateEntry &entry : mStreamSources)
1104 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
1105 lowest = entry.mSource;
1107 if(lowest && lowest->getPriority() < maxprio)
1109 lowest->stop();
1110 if(mMessage.get())
1111 mMessage->sourceForceStopped(lowest);
1114 if(mSourceIds.empty())
1115 throw std::runtime_error("No available sources");
1117 id = mSourceIds.top();
1118 mSourceIds.pop();
1119 return id;
1123 Source ContextImpl::createSource()
1125 CheckContext(this);
1127 SourceImpl *source;
1128 if(!mFreeSources.empty())
1130 source = mFreeSources.front();
1131 mFreeSources.pop();
1133 else
1135 mAllSources.emplace_back(this);
1136 source = &mAllSources.back();
1138 return Source(source);
1142 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
1144 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1145 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1146 { return lhs.mSource < rhs; }
1148 if(iter == mPlaySources.end() || iter->mSource != source)
1149 mPlaySources.insert(iter, {source,id});
1152 void ContextImpl::addPlayingSource(SourceImpl *source)
1154 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1155 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1156 { return lhs.mSource < rhs; }
1158 if(iter == mStreamSources.end() || iter->mSource != source)
1159 mStreamSources.insert(iter, {source});
1162 void ContextImpl::removePlayingSource(SourceImpl *source)
1164 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
1165 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
1166 { return lhs.mSource < rhs; }
1168 if(iter0 != mPlaySources.end() && iter0->mSource == source)
1169 mPlaySources.erase(iter0);
1170 else
1172 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
1173 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
1174 { return lhs.mSource < rhs; }
1176 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1177 mStreamSources.erase(iter1);
1182 void ContextImpl::addStream(SourceImpl *source)
1184 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1185 if(mThread.get_id() == std::thread::id())
1186 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1187 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1188 if(iter == mStreamingSources.end() || *iter != source)
1189 mStreamingSources.insert(iter, source);
1192 void ContextImpl::removeStream(SourceImpl *source)
1194 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1195 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1196 if(iter != mStreamingSources.end() && *iter == source)
1197 mStreamingSources.erase(iter);
1200 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1202 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1203 if(iter != mStreamingSources.end() && *iter == source)
1204 mStreamingSources.erase(iter);
1208 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1210 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
1211 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1212 CheckContext(this);
1214 alGetError();
1215 ALuint id = 0;
1216 alGenAuxiliaryEffectSlots(1, &id);
1217 if(alGetError() != AL_NO_ERROR)
1218 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1219 try {
1220 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1222 catch(...) {
1223 alDeleteAuxiliaryEffectSlots(1, &id);
1224 throw;
1229 Effect ContextImpl::createEffect()
1231 if(!hasExtension(EXT_EFX))
1232 throw std::runtime_error("Effects not supported");
1233 CheckContext(this);
1235 alGetError();
1236 ALuint id = 0;
1237 alGenEffects(1, &id);
1238 if(alGetError() != AL_NO_ERROR)
1239 throw std::runtime_error("Failed to create Effect");
1240 try {
1241 return Effect(new EffectImpl(this, id));
1243 catch(...) {
1244 alDeleteEffects(1, &id);
1245 throw;
1250 SourceGroup ContextImpl::createSourceGroup(String name)
1252 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1253 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1254 { return lhs->getName() < rhs; }
1256 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1257 throw std::runtime_error("Duplicate source group name");
1258 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, std::move(name)));
1259 return SourceGroup(iter->get());
1262 SourceGroup ContextImpl::getSourceGroup(const String &name)
1264 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1265 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1266 { return lhs->getName() < rhs; }
1268 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1269 throw std::runtime_error("Source group not found");
1270 return SourceGroup(iter->get());
1273 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1275 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1276 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1277 { return lhs->getName() < rhs; }
1279 if(iter != mSourceGroups.end() && iter->get() == group)
1280 mSourceGroups.erase(iter);
1284 void ContextImpl::setDopplerFactor(ALfloat factor)
1286 if(!(factor >= 0.0f))
1287 throw std::runtime_error("Doppler factor out of range");
1288 CheckContext(this);
1289 alDopplerFactor(factor);
1293 void ContextImpl::setSpeedOfSound(ALfloat speed)
1295 if(!(speed > 0.0f))
1296 throw std::runtime_error("Speed of sound out of range");
1297 CheckContext(this);
1298 alSpeedOfSound(speed);
1302 void ContextImpl::setDistanceModel(DistanceModel model)
1304 CheckContext(this);
1305 alDistanceModel((ALenum)model);
1309 void ContextImpl::update()
1311 CheckContext(this);
1312 mPlaySources.erase(
1313 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1314 [](const SourceBufferUpdateEntry &entry) -> bool
1315 { return !entry.mSource->playUpdate(entry.mId); }
1316 ), mPlaySources.end()
1318 mStreamSources.erase(
1319 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1320 [](const SourceStreamUpdateEntry &entry) -> bool
1321 { return !entry.mSource->playUpdate(); }
1322 ), mStreamSources.end()
1324 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1326 // For performance reasons, don't wait for the thread's mutex. This
1327 // should be called often enough to keep up with any and all streams
1328 // regardless.
1329 mWakeThread.notify_all();
1332 if(hasExtension(EXT_disconnect) && mIsConnected)
1334 ALCint connected;
1335 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1336 mIsConnected = connected;
1337 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1341 void Context::destroy()
1343 pImpl->destroy();
1344 pImpl = nullptr;
1346 DECL_THUNK0(Device, Context, getDevice,)
1347 DECL_THUNK0(void, Context, startBatch,)
1348 DECL_THUNK0(void, Context, endBatch,)
1349 DECL_THUNK0(Listener, Context, getListener,)
1350 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1351 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1352 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1353 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1354 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, StringView)
1355 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1356 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1357 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1358 DECL_THUNK1(Buffer, Context, getBuffer,, StringView)
1359 DECL_THUNK1(SharedFuture<Buffer>, Context, getBufferAsync,, StringView)
1360 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<StringView>)
1361 DECL_THUNK2(Buffer, Context, createBufferFrom,, StringView, SharedPtr<Decoder>)
1362 DECL_THUNK2(SharedFuture<Buffer>, Context, createBufferAsyncFrom,, StringView, SharedPtr<Decoder>)
1363 DECL_THUNK1(void, Context, removeBuffer,, StringView)
1364 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1365 DECL_THUNK0(Source, Context, createSource,)
1366 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1367 DECL_THUNK0(Effect, Context, createEffect,)
1368 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, String)
1369 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, const String&)
1370 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1371 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1372 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1373 DECL_THUNK0(void, Context, update,)
1376 void Context::MakeCurrent(Context context)
1377 { ContextImpl::MakeCurrent(context.pImpl); }
1379 Context Context::GetCurrent()
1380 { return Context(ContextImpl::GetCurrent()); }
1382 void Context::MakeThreadCurrent(Context context)
1383 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1385 Context Context::GetThreadCurrent()
1386 { return Context(ContextImpl::GetThreadCurrent()); }
1389 void ListenerImpl::setGain(ALfloat gain)
1391 if(!(gain >= 0.0f))
1392 throw std::runtime_error("Gain out of range");
1393 CheckContext(mContext);
1394 alListenerf(AL_GAIN, gain);
1398 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1400 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1401 CheckContext(mContext);
1402 Batcher batcher = mContext->getBatcher();
1403 alListenerfv(AL_POSITION, position.getPtr());
1404 alListenerfv(AL_VELOCITY, velocity.getPtr());
1405 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1408 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1410 CheckContext(mContext);
1411 alListener3f(AL_POSITION, x, y, z);
1414 void ListenerImpl::setPosition(const ALfloat *pos)
1416 CheckContext(mContext);
1417 alListenerfv(AL_POSITION, pos);
1420 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1422 CheckContext(mContext);
1423 alListener3f(AL_VELOCITY, x, y, z);
1426 void ListenerImpl::setVelocity(const ALfloat *vel)
1428 CheckContext(mContext);
1429 alListenerfv(AL_VELOCITY, vel);
1432 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1434 CheckContext(mContext);
1435 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1436 alListenerfv(AL_ORIENTATION, ori);
1439 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1441 CheckContext(mContext);
1442 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1443 alListenerfv(AL_ORIENTATION, ori);
1446 void ListenerImpl::setOrientation(const ALfloat *ori)
1448 CheckContext(mContext);
1449 alListenerfv(AL_ORIENTATION, ori);
1452 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1454 if(!(m_u > 0.0f))
1455 throw std::runtime_error("Invalid meters per unit");
1456 CheckContext(mContext);
1457 if(mContext->hasExtension(EXT_EFX))
1458 alListenerf(AL_METERS_PER_UNIT, m_u);
1462 using Vector3Pair = std::pair<Vector3,Vector3>;
1464 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1465 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1466 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1467 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1468 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1469 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1470 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1471 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1472 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1473 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)