Improve handling the async wake interval
[alure.git] / src / context.cpp
blob089e031cbc368031ce434ac4b631e49e6fb2fa2f
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
52 #ifdef _WIN32
53 // Windows' std::ifstream fails with non-ANSI paths since the standard only
54 // specifies names using const char* (or std::string). MSVC has a non-standard
55 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
56 // but not all Windows compilers support it. So we have to make our own istream
57 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
58 class StreamBuf : public std::streambuf {
59 alure::Array<traits_type::char_type,4096> mBuffer;
60 HANDLE mFile;
62 int_type underflow() override final
64 if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr())
66 // Read in the next chunk of data, and set the pointers on success
67 DWORD got = 0;
68 if(!ReadFile(mFile, mBuffer.data(), mBuffer.size(), &got, NULL))
69 got = 0;
70 setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got);
72 if(gptr() == egptr())
73 return traits_type::eof();
74 return traits_type::to_int_type(*gptr());
77 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override final
79 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
80 return traits_type::eof();
82 LARGE_INTEGER fpos;
83 switch(whence)
85 case std::ios_base::beg:
86 fpos.QuadPart = offset;
87 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
88 return traits_type::eof();
89 break;
91 case std::ios_base::cur:
92 // If the offset remains in the current buffer range, just
93 // update the pointer.
94 if((offset >= 0 && offset < off_type(egptr()-gptr())) ||
95 (offset < 0 && -offset <= off_type(gptr()-eback())))
97 // Get the current file offset to report the correct read
98 // offset.
99 fpos.QuadPart = 0;
100 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
101 return traits_type::eof();
102 setg(eback(), gptr()+offset, egptr());
103 return fpos.QuadPart - off_type(egptr()-gptr());
105 // Need to offset for the file offset being at egptr() while
106 // the requested offset is relative to gptr().
107 offset -= off_type(egptr()-gptr());
108 fpos.QuadPart = offset;
109 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT))
110 return traits_type::eof();
111 break;
113 case std::ios_base::end:
114 fpos.QuadPart = offset;
115 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END))
116 return traits_type::eof();
117 break;
119 default:
120 return traits_type::eof();
122 setg(0, 0, 0);
123 return fpos.QuadPart;
126 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override final
128 // Simplified version of seekoff
129 if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
130 return traits_type::eof();
132 LARGE_INTEGER fpos;
133 fpos.QuadPart = pos;
134 if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN))
135 return traits_type::eof();
137 setg(0, 0, 0);
138 return fpos.QuadPart;
141 public:
142 bool open(const char *filename)
144 alure::Vector<wchar_t> wname;
145 int wnamelen;
147 wnamelen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
148 if(wnamelen <= 0) return false;
150 wname.resize(wnamelen);
151 MultiByteToWideChar(CP_UTF8, 0, filename, -1, wname.data(), wnamelen);
153 mFile = CreateFileW(wname.data(), GENERIC_READ, FILE_SHARE_READ, NULL,
154 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
155 if(mFile == INVALID_HANDLE_VALUE) return false;
156 return true;
159 bool is_open() const { return mFile != INVALID_HANDLE_VALUE; }
161 StreamBuf() : mFile(INVALID_HANDLE_VALUE)
163 ~StreamBuf() override final
165 if(mFile != INVALID_HANDLE_VALUE)
166 CloseHandle(mFile);
167 mFile = INVALID_HANDLE_VALUE;
171 // Inherit from std::istream to use our custom streambuf
172 class Stream : public std::istream {
173 public:
174 Stream(const char *filename) : std::istream(new StreamBuf())
176 // Set the failbit if the file failed to open.
177 if(!(static_cast<StreamBuf*>(rdbuf())->open(filename)))
178 clear(failbit);
180 ~Stream() override final
181 { delete rdbuf(); }
183 bool is_open() const { return static_cast<StreamBuf*>(rdbuf())->is_open(); }
185 #endif
189 namespace alure
192 Decoder::~Decoder() { }
193 DecoderFactory::~DecoderFactory() { }
195 static const std::pair<String,UniquePtr<DecoderFactory>> sDefaultDecoders[] = {
196 { "_alure_int_wave", MakeUnique<WaveDecoderFactory>() },
198 #ifdef HAVE_VORBISFILE
199 { "_alure_int_vorbis", MakeUnique<VorbisFileDecoderFactory>() },
200 #endif
201 #ifdef HAVE_LIBFLAC
202 { "_alure_int_flac", MakeUnique<FlacDecoderFactory>() },
203 #endif
204 #ifdef HAVE_OPUSFILE
205 { "_alure_int_opus", MakeUnique<OpusFileDecoderFactory>() },
206 #endif
207 #ifdef HAVE_LIBSNDFILE
208 { "_alure_int_sndfile", MakeUnique<SndFileDecoderFactory>() },
209 #endif
210 #ifdef HAVE_MPG123
211 { "_alure_int_mpg123", MakeUnique<Mpg123DecoderFactory>() },
212 #endif
215 static std::map<String,UniquePtr<DecoderFactory>> sDecoders;
218 template<typename T>
219 static SharedPtr<Decoder> GetDecoder(const String &name, UniquePtr<std::istream> &file, T start, T end)
221 while(start != end)
223 DecoderFactory *factory = start->second.get();
224 auto decoder = factory->createDecoder(file);
225 if(decoder) return decoder;
227 if(!file || !(file->clear(),file->seekg(0)))
228 throw std::runtime_error("Failed to rewind "+name+" for the next decoder factory");
230 ++start;
233 return nullptr;
236 static SharedPtr<Decoder> GetDecoder(const String &name, UniquePtr<std::istream> file)
238 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
239 if(!decoder) decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
240 if(!decoder) throw std::runtime_error("No decoder for "+name);
241 return decoder;
244 void RegisterDecoder(const String &name, UniquePtr<DecoderFactory> factory)
246 while(sDecoders.find(name) != sDecoders.end())
247 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
248 sDecoders.insert(std::make_pair(name, std::move(factory)));
251 UniquePtr<DecoderFactory> UnregisterDecoder(const String &name)
253 auto iter = sDecoders.find(name);
254 if(iter != sDecoders.end())
256 UniquePtr<DecoderFactory> factory = std::move(iter->second);
257 sDecoders.erase(iter);
258 return factory;
260 return nullptr;
264 FileIOFactory::~FileIOFactory() { }
266 class DefaultFileIOFactory : public FileIOFactory {
267 UniquePtr<std::istream> openFile(const String &name) override final
269 #ifdef _WIN32
270 auto file = MakeUnique<Stream>(name.c_str());
271 #else
272 auto file = MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
273 #endif
274 if(!file->is_open()) file = nullptr;
275 return std::move(file);
278 static DefaultFileIOFactory sDefaultFileFactory;
280 static UniquePtr<FileIOFactory> sFileFactory;
281 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
283 std::swap(sFileFactory, factory);
284 return factory;
287 FileIOFactory &FileIOFactory::get()
289 FileIOFactory *factory = sFileFactory.get();
290 if(factory) return *factory;
291 return sDefaultFileFactory;
295 // Default message handler methods are no-ops.
296 MessageHandler::~MessageHandler()
300 void MessageHandler::deviceDisconnected(Device)
304 void MessageHandler::sourceStopped(Source)
308 void MessageHandler::sourceForceStopped(Source)
312 void MessageHandler::bufferLoading(const String&, ChannelConfig, SampleType, ALuint, const ArrayView<ALbyte>)
316 String MessageHandler::resourceNotFound(const String&)
318 return String();
322 template<typename T>
323 static inline void LoadALFunc(T **func, const char *name)
324 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
326 static void LoadNothing(ALContext*) { }
328 static void LoadEFX(ALContext *ctx)
330 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
331 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
332 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
333 LoadALFunc(&ctx->alEffecti, "alEffecti");
334 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
335 LoadALFunc(&ctx->alEffectf, "alEffectf");
336 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
337 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
338 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
339 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
340 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
342 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
343 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
344 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
345 LoadALFunc(&ctx->alFilteri, "alFilteri");
346 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
347 LoadALFunc(&ctx->alFilterf, "alFilterf");
348 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
349 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
350 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
351 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
352 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
354 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
355 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
356 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
357 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
358 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
359 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
360 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
361 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
362 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
363 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
364 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
367 static void LoadSourceResampler(ALContext *ctx)
369 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
372 static void LoadSourceLatency(ALContext *ctx)
374 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
375 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
378 static const struct {
379 enum ALExtension extension;
380 const char name[32];
381 void (&loader)(ALContext*);
382 } ALExtensionList[] = {
383 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
385 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
386 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
387 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
389 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
390 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
391 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
393 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
394 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
395 { SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
396 { SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
398 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
400 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
401 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
405 ALContext *ALContext::sCurrentCtx = nullptr;
406 thread_local ALContext *ALContext::sThreadCurrentCtx = nullptr;
408 void ALContext::MakeCurrent(ALContext *context)
410 std::unique_lock<std::mutex> lock1, lock2;
411 if(sCurrentCtx)
412 lock1 = std::unique_lock<std::mutex>(sCurrentCtx->mContextMutex);
413 if(context && context != sCurrentCtx)
414 lock2 = std::unique_lock<std::mutex>(context->mContextMutex);
416 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
417 throw std::runtime_error("Call to alcMakeContextCurrent failed");
418 if(context)
420 context->addRef();
421 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
423 std::swap(sCurrentCtx, context);
424 if(context) context->decRef();
426 if(sThreadCurrentCtx)
427 sThreadCurrentCtx->decRef();
428 sThreadCurrentCtx = 0;
430 if(sCurrentCtx && sCurrentCtx != context)
432 lock2.unlock();
433 sCurrentCtx->mWakeThread.notify_all();
437 void ALContext::MakeThreadCurrent(ALContext *context)
439 if(!ALDeviceManager::SetThreadContext)
440 throw std::runtime_error("Thread-local contexts unsupported");
441 if(ALDeviceManager::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
442 throw std::runtime_error("Call to alcSetThreadContext failed");
443 if(context)
445 context->addRef();
446 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
448 if(sThreadCurrentCtx)
449 sThreadCurrentCtx->decRef();
450 sThreadCurrentCtx = context;
453 void ALContext::setupExts()
455 ALCdevice *device = mDevice->getDevice();
456 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
457 for(const auto &entry : ALExtensionList)
459 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
460 alcIsExtensionPresent(device, entry.name) :
461 alIsExtensionPresent(entry.name);
462 if(mHasExt[entry.extension]) entry.loader(this);
467 void ALContext::backgroundProc()
469 if(ALDeviceManager::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
470 ALDeviceManager::SetThreadContext(getContext());
472 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
473 std::chrono::milliseconds waketime(0);
474 std::unique_lock<std::mutex> ctxlock(mContextMutex);
475 while(!mQuitThread.load(std::memory_order_acquire))
478 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
479 auto source = mStreamingSources.begin();
480 while(source != mStreamingSources.end())
482 if(!(*source)->updateAsync())
483 source = mStreamingSources.erase(source);
484 else
485 ++source;
489 // Only do one pending buffer at a time. In case there's several large
490 // buffers to load, we still need to process streaming sources so they
491 // don't underrun.
492 RingBuffer::Data ringdata = mPendingBuffers.get_read_vector()[0];
493 if(ringdata.len > 0)
495 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata.buf);
496 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, pb->mName, this);
497 pb->~PendingBuffer();
498 mPendingBuffers.read_advance(1);
499 continue;
502 std::unique_lock<std::mutex> wakelock(mWakeMutex);
503 if(!mQuitThread.load(std::memory_order_acquire) && mPendingBuffers.read_space() == 0)
505 ctxlock.unlock();
507 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
508 if(interval.count() == 0)
509 mWakeThread.wait(wakelock);
510 else
512 auto now = std::chrono::steady_clock::now() - basetime;
513 if(now > waketime)
515 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
516 waketime += interval * mult;
517 mWakeThread.wait_until(wakelock, waketime + basetime);
520 wakelock.unlock();
522 ctxlock.lock();
523 while(!mQuitThread.load(std::memory_order_acquire) &&
524 alcGetCurrentContext() != getContext())
525 mWakeThread.wait(ctxlock);
528 ctxlock.unlock();
530 if(ALDeviceManager::SetThreadContext)
531 ALDeviceManager::SetThreadContext(nullptr);
535 ALContext::ALContext(ALCcontext *context, ALDevice *device)
536 : mListener(this), mContext(context), mDevice(device), mRefs(0),
537 mHasExt{false}, mPendingBuffers(16, sizeof(PendingBuffer)),
538 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
539 mIsConnected(true), mIsBatching(false),
540 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
541 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
542 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
543 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
544 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
545 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
546 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
547 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
548 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
549 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
553 ALContext::~ALContext()
555 auto ringdata = mPendingBuffers.get_read_vector();
556 if(ringdata[0].len > 0)
558 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
559 for(size_t i = 0;i < ringdata[0].len;i++)
560 pb[i].~PendingBuffer();
561 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
562 for(size_t i = 0;i < ringdata[1].len;i++)
563 pb[i].~PendingBuffer();
565 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
570 void ALContext::destroy()
572 if(mRefs.load() != 0)
573 throw std::runtime_error("Context is in use");
574 if(!mBuffers.empty())
575 throw std::runtime_error("Trying to destroy a context with buffers");
577 if(mThread.joinable())
579 std::unique_lock<std::mutex> lock(mWakeMutex);
580 mQuitThread.store(true, std::memory_order_release);
581 lock.unlock();
582 mWakeThread.notify_all();
583 mThread.join();
586 alcDestroyContext(mContext);
587 mContext = nullptr;
589 mDevice->removeContext(this);
593 void ALContext::startBatch()
595 alcSuspendContext(mContext);
596 mIsBatching = true;
599 void ALContext::endBatch()
601 alcProcessContext(mContext);
602 mIsBatching = false;
606 SharedPtr<MessageHandler> ALContext::setMessageHandler(SharedPtr<MessageHandler> handler)
608 std::lock_guard<std::mutex> lock(mContextMutex);
609 mMessage.swap(handler);
610 return handler;
614 void ALContext::setAsyncWakeInterval(std::chrono::milliseconds msec)
616 if(msec.count() < 0 || msec > std::chrono::milliseconds(1000))
617 throw std::runtime_error("Async wake interval out of range");
618 mWakeInterval.store(msec);
619 mWakeMutex.lock(); mWakeMutex.unlock();
620 mWakeThread.notify_all();
624 SharedPtr<Decoder> ALContext::createDecoder(const String &name)
626 CheckContext(this);
627 auto file = FileIOFactory::get().openFile(name);
628 if(file) return GetDecoder(name, std::move(file));
630 // Resource not found. Try to find a substitute.
631 if(!mMessage.get()) throw std::runtime_error("Failed to open "+name);
632 String oldname = name;
633 do {
634 String newname(mMessage->resourceNotFound(oldname));
635 if(newname.empty())
636 throw std::runtime_error("Failed to open "+oldname);
637 file = FileIOFactory::get().openFile(newname);
638 oldname = std::move(newname);
639 } while(!file);
641 return GetDecoder(oldname, std::move(file));
645 bool ALContext::isSupported(ChannelConfig channels, SampleType type) const
647 CheckContext(this);
648 return GetFormat(channels, type) != AL_NONE;
652 const Vector<String> &ALContext::getAvailableResamplers()
654 CheckContext(this);
655 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
657 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
658 mResamplers.reserve(num_resamplers);
659 for(int i = 0;i < num_resamplers;i++)
660 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
661 if(mResamplers.empty())
662 mResamplers.emplace_back();
664 return mResamplers;
667 ALsizei ALContext::getDefaultResamplerIndex() const
669 CheckContext(this);
670 if(!hasExtension(SOFT_source_resampler))
671 return 0;
672 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
676 BufferOrExceptT ALContext::doCreateBuffer(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
678 BufferOrExceptT retval;
679 ALuint srate = decoder->getFrequency();
680 ChannelConfig chans = decoder->getChannelConfig();
681 SampleType type = decoder->getSampleType();
682 ALuint frames = decoder->getLength();
684 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
685 frames = decoder->read(data.data(), frames);
686 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
687 data.resize(FramesToBytes(frames, chans, type));
689 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
690 if(loop_pts.first >= loop_pts.second)
691 loop_pts = std::make_pair(0, frames);
692 else
694 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
695 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
698 // Get the format before calling the bufferLoading message handler, to
699 // ensure it's something OpenAL can handle.
700 ALenum format = GetFormat(chans, type);
701 if(format == AL_NONE)
703 std::stringstream sstr;
704 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
705 return (retval = std::runtime_error(sstr.str()));
708 if(mMessage.get())
709 mMessage->bufferLoading(name, chans, type, srate, data);
711 alGetError();
712 ALuint bid = 0;
713 alGenBuffers(1, &bid);
714 alBufferData(bid, format, data.data(), data.size(), srate);
715 if(hasExtension(SOFT_loop_points))
717 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
718 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
720 if(alGetError() != AL_NO_ERROR)
722 alDeleteBuffers(1, &bid);
723 return (retval = std::runtime_error("Failed to buffer data"));
726 return (retval = mBuffers.insert(iter,
727 MakeUnique<ALBuffer>(this, bid, srate, chans, type, true, name)
728 )->get());
731 BufferOrExceptT ALContext::doCreateBufferAsync(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
733 BufferOrExceptT retval;
734 ALuint srate = decoder->getFrequency();
735 ChannelConfig chans = decoder->getChannelConfig();
736 SampleType type = decoder->getSampleType();
737 ALuint frames = decoder->getLength();
738 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
740 ALenum format = GetFormat(chans, type);
741 if(format == AL_NONE)
743 std::stringstream sstr;
744 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
745 return (retval = std::runtime_error(sstr.str()));
748 alGetError();
749 ALuint bid = 0;
750 alGenBuffers(1, &bid);
751 if(alGetError() != AL_NO_ERROR)
752 return (retval = std::runtime_error("Failed to create buffer"));
754 auto buffer = MakeUnique<ALBuffer>(this, bid, srate, chans, type, false, name);
756 if(mThread.get_id() == std::thread::id())
757 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
759 while(mPendingBuffers.write_space() == 0)
761 mWakeThread.notify_all();
762 std::this_thread::yield();
765 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
766 new(ringdata.buf) PendingBuffer{name, buffer.get(), decoder, format, frames};
767 mPendingBuffers.write_advance(1);
769 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
772 Buffer ALContext::getBuffer(const String &name)
774 CheckContext(this);
776 auto hasher = std::hash<String>();
777 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
778 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
779 { return hasher(lhs->getName()) < rhs; }
781 if(iter != mBuffers.end() && (*iter)->getName() == name)
783 // Ensure the buffer is loaded before returning. getBuffer guarantees
784 // the returned buffer is loaded.
785 ALBuffer *buffer = iter->get();
786 while(buffer->getLoadStatus() == BufferLoadStatus::Pending)
787 std::this_thread::yield();
788 return Buffer(buffer);
791 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
792 Buffer *buffer = GetIf<Buffer>(&ret);
793 if(EXPECT(!buffer, false))
794 throw Get<std::runtime_error>(ret);
795 return *buffer;
798 Buffer ALContext::getBufferAsync(const String &name)
800 CheckContext(this);
802 auto hasher = std::hash<String>();
803 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
804 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
805 { return hasher(lhs->getName()) < rhs; }
807 if(iter != mBuffers.end() && (*iter)->getName() == name)
808 return Buffer(iter->get());
810 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name));
811 Buffer *buffer = GetIf<Buffer>(&ret);
812 if(EXPECT(!buffer, false))
813 throw Get<std::runtime_error>(ret);
814 mWakeMutex.lock(); mWakeMutex.unlock();
815 mWakeThread.notify_all();
816 return *buffer;
819 void ALContext::precacheBuffersAsync(ArrayView<String> names)
821 CheckContext(this);
823 auto hasher = std::hash<String>();
824 for(const String &name : names)
826 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
827 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
828 { return hasher(lhs->getName()) < rhs; }
830 if(iter != mBuffers.end() && (*iter)->getName() == name)
831 continue;
833 doCreateBufferAsync(name, iter, createDecoder(name));
835 mWakeMutex.lock(); mWakeMutex.unlock();
836 mWakeThread.notify_all();
839 Buffer ALContext::createBufferFrom(const String &name, SharedPtr<Decoder> decoder)
841 CheckContext(this);
843 auto hasher = std::hash<String>();
844 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
845 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
846 { return hasher(lhs->getName()) < rhs; }
848 if(iter != mBuffers.end() && (*iter)->getName() == name)
849 throw std::runtime_error("Buffer \""+name+"\" already exists");
851 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
852 Buffer *buffer = GetIf<Buffer>(&ret);
853 if(EXPECT(!buffer, false))
854 throw Get<std::runtime_error>(ret);
855 return *buffer;
858 Buffer ALContext::createBufferAsyncFrom(const String &name, SharedPtr<Decoder> decoder)
860 CheckContext(this);
862 auto hasher = std::hash<String>();
863 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
864 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
865 { return hasher(lhs->getName()) < rhs; }
867 if(iter != mBuffers.end() && (*iter)->getName() == name)
868 throw std::runtime_error("Buffer \""+name+"\" already exists");
870 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder));
871 Buffer *buffer = GetIf<Buffer>(&ret);
872 if(EXPECT(!buffer, false))
873 throw Get<std::runtime_error>(ret);
874 mWakeMutex.lock(); mWakeMutex.unlock();
875 mWakeThread.notify_all();
876 return *buffer;
880 void ALContext::removeBuffer(const String &name)
882 CheckContext(this);
883 auto hasher = std::hash<String>();
884 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
885 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
886 { return hasher(lhs->getName()) < rhs; }
888 if(iter != mBuffers.end() && (*iter)->getName() == name)
890 (*iter)->cleanup();
891 mBuffers.erase(iter);
896 ALuint ALContext::getSourceId(ALuint maxprio)
898 ALuint id = 0;
899 if(mSourceIds.empty())
901 alGetError();
902 alGenSources(1, &id);
903 if(alGetError() == AL_NO_ERROR)
904 return id;
906 ALSource *lowest = nullptr;
907 for(ALSource *src : mUsedSources)
909 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
910 lowest = src;
912 if(lowest && lowest->getPriority() < maxprio)
914 lowest->stop();
915 if(mMessage.get())
916 mMessage->sourceForceStopped(lowest);
919 if(mSourceIds.empty())
920 throw std::runtime_error("No available sources");
922 id = mSourceIds.top();
923 mSourceIds.pop();
924 return id;
928 Source ALContext::createSource()
930 CheckContext(this);
932 ALSource *source;
933 if(!mFreeSources.empty())
935 source = mFreeSources.front();
936 mFreeSources.pop();
938 else
940 mAllSources.emplace_back(this);
941 source = &mAllSources.back();
943 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
944 if(iter == mUsedSources.end() || *iter != source)
945 mUsedSources.insert(iter, source);
946 return Source(source);
949 void ALContext::freeSource(ALSource *source)
951 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
952 if(iter != mUsedSources.end() && *iter == source) mUsedSources.erase(iter);
953 mFreeSources.push(source);
957 void ALContext::addStream(ALSource *source)
959 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
960 if(mThread.get_id() == std::thread::id())
961 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
962 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
963 if(iter == mStreamingSources.end() || *iter != source)
964 mStreamingSources.insert(iter, source);
967 void ALContext::removeStream(ALSource *source)
969 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
970 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
971 if(iter != mStreamingSources.end() && *iter == source)
972 mStreamingSources.erase(iter);
975 void ALContext::removeStreamNoLock(ALSource *source)
977 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
978 if(iter != mStreamingSources.end() && *iter == source)
979 mStreamingSources.erase(iter);
983 AuxiliaryEffectSlot ALContext::createAuxiliaryEffectSlot()
985 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
986 throw std::runtime_error("AuxiliaryEffectSlots not supported");
987 CheckContext(this);
989 alGetError();
990 ALuint id = 0;
991 alGenAuxiliaryEffectSlots(1, &id);
992 if(alGetError() != AL_NO_ERROR)
993 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
994 try {
995 return AuxiliaryEffectSlot(new ALAuxiliaryEffectSlot(this, id));
997 catch(...) {
998 alDeleteAuxiliaryEffectSlots(1, &id);
999 throw;
1004 Effect ALContext::createEffect()
1006 if(!hasExtension(EXT_EFX))
1007 throw std::runtime_error("Effects not supported");
1008 CheckContext(this);
1010 alGetError();
1011 ALuint id = 0;
1012 alGenEffects(1, &id);
1013 if(alGetError() != AL_NO_ERROR)
1014 throw std::runtime_error("Failed to create Effect");
1015 try {
1016 return Effect(new ALEffect(this, id));
1018 catch(...) {
1019 alDeleteEffects(1, &id);
1020 throw;
1025 SourceGroup ALContext::createSourceGroup(String name)
1027 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1028 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1029 { return lhs->getName() < rhs; }
1031 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1032 throw std::runtime_error("Duplicate source group name");
1033 iter = mSourceGroups.insert(iter, MakeUnique<ALSourceGroup>(this, std::move(name)));
1034 return SourceGroup(iter->get());
1037 SourceGroup ALContext::getSourceGroup(const String &name)
1039 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1040 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1041 { return lhs->getName() < rhs; }
1043 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1044 throw std::runtime_error("Source group not found");
1045 return SourceGroup(iter->get());
1048 void ALContext::freeSourceGroup(ALSourceGroup *group)
1050 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1051 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1052 { return lhs->getName() < rhs; }
1054 if(iter != mSourceGroups.end() && iter->get() == group)
1055 mSourceGroups.erase(iter);
1059 void ALContext::setDopplerFactor(ALfloat factor)
1061 if(!(factor >= 0.0f))
1062 throw std::runtime_error("Doppler factor out of range");
1063 CheckContext(this);
1064 alDopplerFactor(factor);
1068 void ALContext::setSpeedOfSound(ALfloat speed)
1070 if(!(speed > 0.0f))
1071 throw std::runtime_error("Speed of sound out of range");
1072 CheckContext(this);
1073 alSpeedOfSound(speed);
1077 void ALContext::setDistanceModel(DistanceModel model)
1079 CheckContext(this);
1080 alDistanceModel((ALenum)model);
1084 void ALContext::update()
1086 CheckContext(this);
1087 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fn(&ALSource::updateNoCtxCheck));
1088 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1090 // For performance reasons, don't wait for the thread's mutex. This
1091 // should be called often enough to keep up with any and all streams
1092 // regardless.
1093 mWakeThread.notify_all();
1096 if(hasExtension(EXT_disconnect) && mIsConnected)
1098 ALCint connected;
1099 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1100 mIsConnected = connected;
1101 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1105 void Context::destroy()
1107 pImpl->destroy();
1108 pImpl = nullptr;
1110 DECL_THUNK0(Device, Context, getDevice,)
1111 DECL_THUNK0(void, Context, startBatch,)
1112 DECL_THUNK0(void, Context, endBatch,)
1113 DECL_THUNK0(Listener, Context, getListener,)
1114 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1115 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1116 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1117 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1118 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, const String&)
1119 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1120 DECL_THUNK0(const Vector<String>&, Context, getAvailableResamplers,)
1121 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1122 DECL_THUNK1(Buffer, Context, getBuffer,, const String&)
1123 DECL_THUNK1(Buffer, Context, getBufferAsync,, const String&)
1124 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<String>)
1125 DECL_THUNK2(Buffer, Context, createBufferFrom,, const String&, SharedPtr<Decoder>)
1126 DECL_THUNK2(Buffer, Context, createBufferAsyncFrom,, const String&, SharedPtr<Decoder>)
1127 DECL_THUNK1(void, Context, removeBuffer,, const String&)
1128 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1129 DECL_THUNK0(Source, Context, createSource,)
1130 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1131 DECL_THUNK0(Effect, Context, createEffect,)
1132 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, String)
1133 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, const String&)
1134 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1135 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1136 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1137 DECL_THUNK0(void, Context, update,)
1140 void Context::MakeCurrent(Context context)
1141 { ALContext::MakeCurrent(context.pImpl); }
1143 Context Context::GetCurrent()
1144 { return Context(ALContext::GetCurrent()); }
1146 void Context::MakeThreadCurrent(Context context)
1147 { ALContext::MakeThreadCurrent(context.pImpl); }
1149 Context Context::GetThreadCurrent()
1150 { return Context(ALContext::GetThreadCurrent()); }
1153 void ALListener::setGain(ALfloat gain)
1155 if(!(gain >= 0.0f))
1156 throw std::runtime_error("Gain out of range");
1157 CheckContext(mContext);
1158 alListenerf(AL_GAIN, gain);
1162 void ALListener::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1164 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1165 CheckContext(mContext);
1166 Batcher batcher = mContext->getBatcher();
1167 alListenerfv(AL_POSITION, position.getPtr());
1168 alListenerfv(AL_VELOCITY, velocity.getPtr());
1169 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1172 void ALListener::setPosition(ALfloat x, ALfloat y, ALfloat z)
1174 CheckContext(mContext);
1175 alListener3f(AL_POSITION, x, y, z);
1178 void ALListener::setPosition(const ALfloat *pos)
1180 CheckContext(mContext);
1181 alListenerfv(AL_POSITION, pos);
1184 void ALListener::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1186 CheckContext(mContext);
1187 alListener3f(AL_VELOCITY, x, y, z);
1190 void ALListener::setVelocity(const ALfloat *vel)
1192 CheckContext(mContext);
1193 alListenerfv(AL_VELOCITY, vel);
1196 void ALListener::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1198 CheckContext(mContext);
1199 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1200 alListenerfv(AL_ORIENTATION, ori);
1203 void ALListener::setOrientation(const ALfloat *at, const ALfloat *up)
1205 CheckContext(mContext);
1206 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1207 alListenerfv(AL_ORIENTATION, ori);
1210 void ALListener::setOrientation(const ALfloat *ori)
1212 CheckContext(mContext);
1213 alListenerfv(AL_ORIENTATION, ori);
1216 void ALListener::setMetersPerUnit(ALfloat m_u)
1218 if(!(m_u > 0.0f))
1219 throw std::runtime_error("Invalid meters per unit");
1220 CheckContext(mContext);
1221 if(mContext->hasExtension(EXT_EFX))
1222 alListenerf(AL_METERS_PER_UNIT, m_u);
1226 using Vector3Pair = std::pair<Vector3,Vector3>;
1228 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1229 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1230 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1231 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1232 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1233 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1234 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1235 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1236 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1237 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)