Return the exception from doCreateBuffer functions instead of throwing
[alure.git] / src / context.cpp
blobafa4a5970592246fa90c13d4fce7946d6dda3959
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 Vector<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();
508 if(interval.count() == 0)
509 mWakeThread.wait(wakelock);
510 else
512 auto now = std::chrono::steady_clock::now() - basetime;
513 while((waketime - now).count() <= 0) waketime += interval;
514 mWakeThread.wait_until(wakelock, waketime + basetime);
516 wakelock.unlock();
518 ctxlock.lock();
519 while(!mQuitThread.load(std::memory_order_acquire) &&
520 alcGetCurrentContext() != getContext())
521 mWakeThread.wait(ctxlock);
524 ctxlock.unlock();
526 if(ALDeviceManager::SetThreadContext)
527 ALDeviceManager::SetThreadContext(nullptr);
531 ALContext::ALContext(ALCcontext *context, ALDevice *device)
532 : mListener(this), mContext(context), mDevice(device), mRefs(0),
533 mHasExt{false}, mPendingBuffers(16, sizeof(PendingBuffer)),
534 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
535 mIsConnected(true), mIsBatching(false),
536 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
537 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
538 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
539 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
540 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
541 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
542 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
543 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
544 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
545 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
549 ALContext::~ALContext()
551 auto ringdata = mPendingBuffers.get_read_vector();
552 if(ringdata[0].len > 0)
554 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
555 for(size_t i = 0;i < ringdata[0].len;i++)
556 pb[i].~PendingBuffer();
557 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
558 for(size_t i = 0;i < ringdata[1].len;i++)
559 pb[i].~PendingBuffer();
561 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
566 void ALContext::destroy()
568 if(mRefs.load() != 0)
569 throw std::runtime_error("Context is in use");
570 if(!mBuffers.empty())
571 throw std::runtime_error("Trying to destroy a context with buffers");
573 if(mThread.joinable())
575 std::unique_lock<std::mutex> lock(mWakeMutex);
576 mQuitThread.store(true, std::memory_order_release);
577 lock.unlock();
578 mWakeThread.notify_all();
579 mThread.join();
582 alcDestroyContext(mContext);
583 mContext = nullptr;
585 mDevice->removeContext(this);
589 void ALContext::startBatch()
591 alcSuspendContext(mContext);
592 mIsBatching = true;
595 void ALContext::endBatch()
597 alcProcessContext(mContext);
598 mIsBatching = false;
602 SharedPtr<MessageHandler> ALContext::setMessageHandler(SharedPtr<MessageHandler> handler)
604 std::lock_guard<std::mutex> lock(mContextMutex);
605 mMessage.swap(handler);
606 return handler;
610 void ALContext::setAsyncWakeInterval(std::chrono::milliseconds msec)
612 mWakeInterval.store(msec);
613 mWakeMutex.lock(); mWakeMutex.unlock();
614 mWakeThread.notify_all();
618 SharedPtr<Decoder> ALContext::createDecoder(const String &name)
620 CheckContext(this);
621 auto file = FileIOFactory::get().openFile(name);
622 if(file) return GetDecoder(name, std::move(file));
624 // Resource not found. Try to find a substitute.
625 if(!mMessage.get()) throw std::runtime_error("Failed to open "+name);
626 String oldname = name;
627 do {
628 String newname(mMessage->resourceNotFound(oldname));
629 if(newname.empty())
630 throw std::runtime_error("Failed to open "+oldname);
631 file = FileIOFactory::get().openFile(newname);
632 oldname = std::move(newname);
633 } while(!file);
635 return GetDecoder(oldname, std::move(file));
639 bool ALContext::isSupported(ChannelConfig channels, SampleType type) const
641 CheckContext(this);
642 return GetFormat(channels, type) != AL_NONE;
646 const Vector<String> &ALContext::getAvailableResamplers()
648 CheckContext(this);
649 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
651 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
652 mResamplers.reserve(num_resamplers);
653 for(int i = 0;i < num_resamplers;i++)
654 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
655 if(mResamplers.empty())
656 mResamplers.emplace_back();
658 return mResamplers;
661 ALsizei ALContext::getDefaultResamplerIndex() const
663 CheckContext(this);
664 if(!hasExtension(SOFT_source_resampler))
665 return 0;
666 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
670 BufferOrExceptT ALContext::doCreateBuffer(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
672 BufferOrExceptT retval;
673 ALuint srate = decoder->getFrequency();
674 ChannelConfig chans = decoder->getChannelConfig();
675 SampleType type = decoder->getSampleType();
676 ALuint frames = decoder->getLength();
678 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
679 frames = decoder->read(&data[0], frames);
680 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
681 data.resize(FramesToBytes(frames, chans, type));
683 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
684 if(loop_pts.first >= loop_pts.second)
685 loop_pts = std::make_pair(0, frames);
686 else
688 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
689 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
692 // Get the format before calling the bufferLoading message handler, to
693 // ensure it's something OpenAL can handle.
694 ALenum format = GetFormat(chans, type);
695 if(format == AL_NONE)
697 std::stringstream sstr;
698 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
699 return (retval = std::runtime_error(sstr.str()));
702 if(mMessage.get())
703 mMessage->bufferLoading(name, chans, type, srate, data);
705 alGetError();
706 ALuint bid = 0;
707 alGenBuffers(1, &bid);
708 alBufferData(bid, format, &data[0], data.size(), srate);
709 if(hasExtension(SOFT_loop_points))
711 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
712 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
714 if(alGetError() != AL_NO_ERROR)
716 alDeleteBuffers(1, &bid);
717 return (retval = std::runtime_error("Failed to buffer data"));
720 return (retval = mBuffers.insert(iter,
721 MakeUnique<ALBuffer>(this, bid, srate, chans, type, true, name)
722 )->get());
725 BufferOrExceptT ALContext::doCreateBufferAsync(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
727 BufferOrExceptT retval;
728 ALuint srate = decoder->getFrequency();
729 ChannelConfig chans = decoder->getChannelConfig();
730 SampleType type = decoder->getSampleType();
731 ALuint frames = decoder->getLength();
732 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
734 ALenum format = GetFormat(chans, type);
735 if(format == AL_NONE)
737 std::stringstream sstr;
738 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
739 return (retval = std::runtime_error(sstr.str()));
742 alGetError();
743 ALuint bid = 0;
744 alGenBuffers(1, &bid);
745 if(alGetError() != AL_NO_ERROR)
746 return (retval = std::runtime_error("Failed to buffer data"));
748 auto buffer = MakeUnique<ALBuffer>(this, bid, srate, chans, type, false, name);
750 if(mThread.get_id() == std::thread::id())
751 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
753 while(mPendingBuffers.write_space() == 0)
755 mWakeThread.notify_all();
756 std::this_thread::yield();
759 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
760 new(ringdata.buf) PendingBuffer{name, buffer.get(), decoder, format, frames};
761 mPendingBuffers.write_advance(1);
763 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
766 Buffer ALContext::getBuffer(const String &name)
768 CheckContext(this);
770 auto hasher = std::hash<String>();
771 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
772 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
773 { return hasher(lhs->getName()) < rhs; }
775 if(iter != mBuffers.end() && (*iter)->getName() == name)
777 // Ensure the buffer is loaded before returning. getBuffer guarantees
778 // the returned buffer is loaded.
779 ALBuffer *buffer = iter->get();
780 while(buffer->getLoadStatus() == BufferLoadStatus::Pending)
781 std::this_thread::yield();
782 return Buffer(buffer);
785 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
786 Buffer *buffer = GetIf<Buffer>(&ret);
787 if(EXPECT(!buffer, false))
788 throw Get<std::runtime_error>(ret);
789 return *buffer;
792 Buffer ALContext::getBufferAsync(const String &name)
794 CheckContext(this);
796 auto hasher = std::hash<String>();
797 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
798 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
799 { return hasher(lhs->getName()) < rhs; }
801 if(iter != mBuffers.end() && (*iter)->getName() == name)
802 return Buffer(iter->get());
804 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name));
805 Buffer *buffer = GetIf<Buffer>(&ret);
806 if(EXPECT(!buffer, false))
807 throw Get<std::runtime_error>(ret);
808 mWakeMutex.lock(); mWakeMutex.unlock();
809 mWakeThread.notify_all();
810 return *buffer;
813 Buffer ALContext::createBufferFrom(const String &name, SharedPtr<Decoder> decoder)
815 CheckContext(this);
817 auto hasher = std::hash<String>();
818 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
819 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
820 { return hasher(lhs->getName()) < rhs; }
822 if(iter != mBuffers.end() && (*iter)->getName() == name)
823 throw std::runtime_error("Buffer \""+name+"\" already exists");
825 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
826 Buffer *buffer = GetIf<Buffer>(&ret);
827 if(EXPECT(!buffer, false))
828 throw Get<std::runtime_error>(ret);
829 return *buffer;
832 Buffer ALContext::createBufferAsyncFrom(const String &name, SharedPtr<Decoder> decoder)
834 CheckContext(this);
836 auto hasher = std::hash<String>();
837 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
838 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
839 { return hasher(lhs->getName()) < rhs; }
841 if(iter != mBuffers.end() && (*iter)->getName() == name)
842 throw std::runtime_error("Buffer \""+name+"\" already exists");
844 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder));
845 Buffer *buffer = GetIf<Buffer>(&ret);
846 if(EXPECT(!buffer, false))
847 throw Get<std::runtime_error>(ret);
848 mWakeMutex.lock(); mWakeMutex.unlock();
849 mWakeThread.notify_all();
850 return *buffer;
854 void ALContext::removeBuffer(const String &name)
856 CheckContext(this);
857 auto hasher = std::hash<String>();
858 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
859 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
860 { return hasher(lhs->getName()) < rhs; }
862 if(iter != mBuffers.end() && (*iter)->getName() == name)
864 (*iter)->cleanup();
865 mBuffers.erase(iter);
870 ALuint ALContext::getSourceId(ALuint maxprio)
872 ALuint id = 0;
873 if(mSourceIds.empty())
875 alGetError();
876 alGenSources(1, &id);
877 if(alGetError() == AL_NO_ERROR)
878 return id;
880 ALSource *lowest = nullptr;
881 for(ALSource *src : mUsedSources)
883 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
884 lowest = src;
886 if(lowest && lowest->getPriority() < maxprio)
888 lowest->stop();
889 if(mMessage.get())
890 mMessage->sourceForceStopped(lowest);
893 if(mSourceIds.empty())
894 throw std::runtime_error("No available sources");
896 id = mSourceIds.top();
897 mSourceIds.pop();
898 return id;
902 Source ALContext::createSource()
904 CheckContext(this);
906 ALSource *source;
907 if(!mFreeSources.empty())
909 source = mFreeSources.front();
910 mFreeSources.pop();
912 else
914 mAllSources.emplace_back(this);
915 source = &mAllSources.back();
917 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
918 if(iter == mUsedSources.end() || *iter != source)
919 mUsedSources.insert(iter, source);
920 return Source(source);
923 void ALContext::freeSource(ALSource *source)
925 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
926 if(iter != mUsedSources.end() && *iter == source) mUsedSources.erase(iter);
927 mFreeSources.push(source);
931 void ALContext::addStream(ALSource *source)
933 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
934 if(mThread.get_id() == std::thread::id())
935 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
936 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
937 if(iter == mStreamingSources.end() || *iter != source)
938 mStreamingSources.insert(iter, source);
941 void ALContext::removeStream(ALSource *source)
943 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
944 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
945 if(iter != mStreamingSources.end() && *iter == source)
946 mStreamingSources.erase(iter);
949 void ALContext::removeStreamNoLock(ALSource *source)
951 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
952 if(iter != mStreamingSources.end() && *iter == source)
953 mStreamingSources.erase(iter);
957 AuxiliaryEffectSlot ALContext::createAuxiliaryEffectSlot()
959 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
960 throw std::runtime_error("AuxiliaryEffectSlots not supported");
961 CheckContext(this);
963 alGetError();
964 ALuint id = 0;
965 alGenAuxiliaryEffectSlots(1, &id);
966 if(alGetError() != AL_NO_ERROR)
967 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
968 try {
969 return AuxiliaryEffectSlot(new ALAuxiliaryEffectSlot(this, id));
971 catch(...) {
972 alDeleteAuxiliaryEffectSlots(1, &id);
973 throw;
978 Effect ALContext::createEffect()
980 if(!hasExtension(EXT_EFX))
981 throw std::runtime_error("Effects not supported");
982 CheckContext(this);
984 alGetError();
985 ALuint id = 0;
986 alGenEffects(1, &id);
987 if(alGetError() != AL_NO_ERROR)
988 throw std::runtime_error("Failed to create Effect");
989 try {
990 return Effect(new ALEffect(this, id));
992 catch(...) {
993 alDeleteEffects(1, &id);
994 throw;
999 SourceGroup ALContext::createSourceGroup(String name)
1001 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1002 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1003 { return lhs->getName() < rhs; }
1005 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1006 throw std::runtime_error("Duplicate source group name");
1007 iter = mSourceGroups.insert(iter, MakeUnique<ALSourceGroup>(this, std::move(name)));
1008 return SourceGroup(iter->get());
1011 SourceGroup ALContext::getSourceGroup(const String &name)
1013 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1014 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1015 { return lhs->getName() < rhs; }
1017 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1018 throw std::runtime_error("Source group not found");
1019 return SourceGroup(iter->get());
1022 void ALContext::freeSourceGroup(ALSourceGroup *group)
1024 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1025 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1026 { return lhs->getName() < rhs; }
1028 if(iter != mSourceGroups.end() && iter->get() == group)
1029 mSourceGroups.erase(iter);
1033 void ALContext::setDopplerFactor(ALfloat factor)
1035 if(!(factor >= 0.0f))
1036 throw std::runtime_error("Doppler factor out of range");
1037 CheckContext(this);
1038 alDopplerFactor(factor);
1042 void ALContext::setSpeedOfSound(ALfloat speed)
1044 if(!(speed > 0.0f))
1045 throw std::runtime_error("Speed of sound out of range");
1046 CheckContext(this);
1047 alSpeedOfSound(speed);
1051 void ALContext::setDistanceModel(DistanceModel model)
1053 CheckContext(this);
1054 alDistanceModel((ALenum)model);
1058 void ALContext::update()
1060 CheckContext(this);
1061 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fn(&ALSource::updateNoCtxCheck));
1062 if(!mWakeInterval.load().count())
1064 // For performance reasons, don't wait for the thread's mutex. This
1065 // should be called often enough to keep up with any and all streams
1066 // regardless.
1067 mWakeThread.notify_all();
1070 if(hasExtension(EXT_disconnect) && mIsConnected)
1072 ALCint connected;
1073 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1074 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1075 mIsConnected = connected;
1079 void Context::destroy()
1081 pImpl->destroy();
1082 pImpl = nullptr;
1084 DECL_THUNK0(Device, Context, getDevice,)
1085 DECL_THUNK0(void, Context, startBatch,)
1086 DECL_THUNK0(void, Context, endBatch,)
1087 DECL_THUNK0(Listener, Context, getListener,)
1088 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1089 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1090 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1091 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1092 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, const String&)
1093 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1094 DECL_THUNK0(const Vector<String>&, Context, getAvailableResamplers,)
1095 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1096 DECL_THUNK1(Buffer, Context, getBuffer,, const String&)
1097 DECL_THUNK1(Buffer, Context, getBufferAsync,, const String&)
1098 DECL_THUNK2(Buffer, Context, createBufferFrom,, const String&, SharedPtr<Decoder>)
1099 DECL_THUNK2(Buffer, Context, createBufferAsyncFrom,, const String&, SharedPtr<Decoder>)
1100 DECL_THUNK1(void, Context, removeBuffer,, const String&)
1101 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1102 DECL_THUNK0(Source, Context, createSource,)
1103 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1104 DECL_THUNK0(Effect, Context, createEffect,)
1105 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, String)
1106 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, const String&)
1107 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1108 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1109 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1110 DECL_THUNK0(void, Context, update,)
1113 void Context::MakeCurrent(Context context)
1114 { ALContext::MakeCurrent(context.pImpl); }
1116 Context Context::GetCurrent()
1117 { return Context(ALContext::GetCurrent()); }
1119 void Context::MakeThreadCurrent(Context context)
1120 { ALContext::MakeThreadCurrent(context.pImpl); }
1122 Context Context::GetThreadCurrent()
1123 { return Context(ALContext::GetThreadCurrent()); }
1126 void ALListener::setGain(ALfloat gain)
1128 if(!(gain >= 0.0f))
1129 throw std::runtime_error("Gain out of range");
1130 CheckContext(mContext);
1131 alListenerf(AL_GAIN, gain);
1135 void ALListener::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1137 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1138 CheckContext(mContext);
1139 Batcher batcher = mContext->getBatcher();
1140 alListenerfv(AL_POSITION, position.getPtr());
1141 alListenerfv(AL_VELOCITY, velocity.getPtr());
1142 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1145 void ALListener::setPosition(ALfloat x, ALfloat y, ALfloat z)
1147 CheckContext(mContext);
1148 alListener3f(AL_POSITION, x, y, z);
1151 void ALListener::setPosition(const ALfloat *pos)
1153 CheckContext(mContext);
1154 alListenerfv(AL_POSITION, pos);
1157 void ALListener::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1159 CheckContext(mContext);
1160 alListener3f(AL_VELOCITY, x, y, z);
1163 void ALListener::setVelocity(const ALfloat *vel)
1165 CheckContext(mContext);
1166 alListenerfv(AL_VELOCITY, vel);
1169 void ALListener::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1171 CheckContext(mContext);
1172 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1173 alListenerfv(AL_ORIENTATION, ori);
1176 void ALListener::setOrientation(const ALfloat *at, const ALfloat *up)
1178 CheckContext(mContext);
1179 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1180 alListenerfv(AL_ORIENTATION, ori);
1183 void ALListener::setOrientation(const ALfloat *ori)
1185 CheckContext(mContext);
1186 alListenerfv(AL_ORIENTATION, ori);
1189 void ALListener::setMetersPerUnit(ALfloat m_u)
1191 if(!(m_u > 0.0f))
1192 throw std::runtime_error("Invalid meters per unit");
1193 CheckContext(mContext);
1194 if(mContext->hasExtension(EXT_EFX))
1195 alListenerf(AL_METERS_PER_UNIT, m_u);
1199 using Vector3Pair = std::pair<Vector3,Vector3>;
1201 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1202 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1203 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1204 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1205 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1206 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1207 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1208 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1209 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1210 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)