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