Don't throw if a file or decoder fails to open in precacheBuffersAsync
[alure.git] / src / context.cpp
blobd53fafdf525920bf73999c1afb11ed38aee89384
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 DecoderOrExceptT GetDecoder(const String &name, UniquePtr<std::istream> &file, T start, T end)
221 DecoderOrExceptT ret;
222 while(start != end)
224 DecoderFactory *factory = start->second.get();
225 auto decoder = factory->createDecoder(file);
226 if(decoder) return (ret = std::move(decoder));
228 if(!file || !(file->clear(),file->seekg(0)))
229 return (ret = std::runtime_error("Failed to rewind "+name+" for the next decoder factory"));
231 ++start;
234 return (ret = nullptr);
237 static DecoderOrExceptT GetDecoder(const String &name, UniquePtr<std::istream> file)
239 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
240 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
241 if(std::get<SharedPtr<Decoder>>(decoder)) return decoder;
242 decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
243 if(std::holds_alternative<std::runtime_error>(decoder)) return decoder;
244 if(std::get<SharedPtr<Decoder>>(decoder)) return decoder;
245 return (decoder = std::runtime_error("No decoder for "+name));
248 void RegisterDecoder(const String &name, UniquePtr<DecoderFactory> factory)
250 while(sDecoders.find(name) != sDecoders.end())
251 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
252 sDecoders.insert(std::make_pair(name, std::move(factory)));
255 UniquePtr<DecoderFactory> UnregisterDecoder(const String &name)
257 auto iter = sDecoders.find(name);
258 if(iter != sDecoders.end())
260 UniquePtr<DecoderFactory> factory = std::move(iter->second);
261 sDecoders.erase(iter);
262 return factory;
264 return nullptr;
268 FileIOFactory::~FileIOFactory() { }
270 class DefaultFileIOFactory : public FileIOFactory {
271 UniquePtr<std::istream> openFile(const String &name) override final
273 #ifdef _WIN32
274 auto file = MakeUnique<Stream>(name.c_str());
275 #else
276 auto file = MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
277 #endif
278 if(!file->is_open()) file = nullptr;
279 return std::move(file);
282 static DefaultFileIOFactory sDefaultFileFactory;
284 static UniquePtr<FileIOFactory> sFileFactory;
285 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
287 std::swap(sFileFactory, factory);
288 return factory;
291 FileIOFactory &FileIOFactory::get()
293 FileIOFactory *factory = sFileFactory.get();
294 if(factory) return *factory;
295 return sDefaultFileFactory;
299 // Default message handler methods are no-ops.
300 MessageHandler::~MessageHandler()
304 void MessageHandler::deviceDisconnected(Device)
308 void MessageHandler::sourceStopped(Source)
312 void MessageHandler::sourceForceStopped(Source)
316 void MessageHandler::bufferLoading(const String&, ChannelConfig, SampleType, ALuint, const ArrayView<ALbyte>)
320 String MessageHandler::resourceNotFound(const String&)
322 return String();
326 template<typename T>
327 static inline void LoadALFunc(T **func, const char *name)
328 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
330 static void LoadNothing(ContextImpl*) { }
332 static void LoadEFX(ContextImpl *ctx)
334 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
335 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
336 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
337 LoadALFunc(&ctx->alEffecti, "alEffecti");
338 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
339 LoadALFunc(&ctx->alEffectf, "alEffectf");
340 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
341 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
342 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
343 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
344 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
346 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
347 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
348 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
349 LoadALFunc(&ctx->alFilteri, "alFilteri");
350 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
351 LoadALFunc(&ctx->alFilterf, "alFilterf");
352 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
353 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
354 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
355 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
356 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
358 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
359 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
360 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
361 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
362 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
363 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
364 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
365 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
366 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
367 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
368 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
371 static void LoadSourceResampler(ContextImpl *ctx)
373 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
376 static void LoadSourceLatency(ContextImpl *ctx)
378 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
379 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
382 static const struct {
383 enum ALExtension extension;
384 const char name[32];
385 void (&loader)(ContextImpl*);
386 } ALExtensionList[] = {
387 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
389 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
390 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
391 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
393 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
394 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
395 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
397 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
398 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
399 { SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
400 { SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
402 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
404 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
405 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
409 ContextImpl *ContextImpl::sCurrentCtx = nullptr;
410 thread_local ContextImpl *ContextImpl::sThreadCurrentCtx = nullptr;
412 void ContextImpl::MakeCurrent(ContextImpl *context)
414 std::unique_lock<std::mutex> lock1, lock2;
415 if(sCurrentCtx)
416 lock1 = std::unique_lock<std::mutex>(sCurrentCtx->mContextMutex);
417 if(context && context != sCurrentCtx)
418 lock2 = std::unique_lock<std::mutex>(context->mContextMutex);
420 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
421 throw std::runtime_error("Call to alcMakeContextCurrent failed");
422 if(context)
424 context->addRef();
425 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
427 std::swap(sCurrentCtx, context);
428 if(context) context->decRef();
430 if(sThreadCurrentCtx)
431 sThreadCurrentCtx->decRef();
432 sThreadCurrentCtx = 0;
434 if(sCurrentCtx && sCurrentCtx != context)
436 lock2.unlock();
437 sCurrentCtx->mWakeThread.notify_all();
441 void ContextImpl::MakeThreadCurrent(ContextImpl *context)
443 if(!DeviceManagerImpl::SetThreadContext)
444 throw std::runtime_error("Thread-local contexts unsupported");
445 if(DeviceManagerImpl::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
446 throw std::runtime_error("Call to alcSetThreadContext failed");
447 if(context)
449 context->addRef();
450 std::call_once(context->mSetExts, std::mem_fn(&ContextImpl::setupExts), context);
452 if(sThreadCurrentCtx)
453 sThreadCurrentCtx->decRef();
454 sThreadCurrentCtx = context;
457 void ContextImpl::setupExts()
459 ALCdevice *device = mDevice->getDevice();
460 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
461 for(const auto &entry : ALExtensionList)
463 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
464 alcIsExtensionPresent(device, entry.name) :
465 alIsExtensionPresent(entry.name);
466 if(mHasExt[entry.extension]) entry.loader(this);
471 void ContextImpl::backgroundProc()
473 if(DeviceManagerImpl::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
474 DeviceManagerImpl::SetThreadContext(getContext());
476 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
477 std::chrono::milliseconds waketime(0);
478 std::unique_lock<std::mutex> ctxlock(mContextMutex);
479 while(!mQuitThread.load(std::memory_order_acquire))
482 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
483 mStreamingSources.erase(
484 std::remove_if(mStreamingSources.begin(), mStreamingSources.end(),
485 [](SourceImpl *source) -> bool
486 { return !source->updateAsync(); }
487 ), mStreamingSources.end()
491 // Only do one pending buffer at a time. In case there's several large
492 // buffers to load, we still need to process streaming sources so they
493 // don't underrun.
494 RingBuffer::Data ringdata = mPendingBuffers.get_read_vector()[0];
495 if(ringdata.len > 0)
497 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata.buf);
498 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, pb->mName, this);
499 pb->~PendingBuffer();
500 mPendingBuffers.read_advance(1);
501 continue;
504 std::unique_lock<std::mutex> wakelock(mWakeMutex);
505 if(!mQuitThread.load(std::memory_order_acquire) && mPendingBuffers.read_space() == 0)
507 ctxlock.unlock();
509 std::chrono::milliseconds interval = mWakeInterval.load(std::memory_order_relaxed);
510 if(interval.count() == 0)
511 mWakeThread.wait(wakelock);
512 else
514 auto now = std::chrono::steady_clock::now() - basetime;
515 if(now > waketime)
517 auto mult = (now-waketime + interval-std::chrono::milliseconds(1)) / interval;
518 waketime += interval * mult;
519 mWakeThread.wait_until(wakelock, waketime + basetime);
522 wakelock.unlock();
524 ctxlock.lock();
525 while(!mQuitThread.load(std::memory_order_acquire) &&
526 alcGetCurrentContext() != getContext())
527 mWakeThread.wait(ctxlock);
530 ctxlock.unlock();
532 if(DeviceManagerImpl::SetThreadContext)
533 DeviceManagerImpl::SetThreadContext(nullptr);
537 ContextImpl::ContextImpl(ALCcontext *context, DeviceImpl *device)
538 : mListener(this), mContext(context), mDevice(device),
539 mWakeInterval(std::chrono::milliseconds::zero()),
540 mPendingBuffers(16, sizeof(PendingBuffer)), mQuitThread(false),
541 mRefs(0), mHasExt{false}, mIsConnected(true), mIsBatching(false),
542 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
543 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
544 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
545 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
546 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
547 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
548 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
549 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
550 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
551 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
555 ContextImpl::~ContextImpl()
557 auto ringdata = mPendingBuffers.get_read_vector();
558 if(ringdata[0].len > 0)
560 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
561 for(size_t i = 0;i < ringdata[0].len;i++)
562 pb[i].~PendingBuffer();
563 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
564 for(size_t i = 0;i < ringdata[1].len;i++)
565 pb[i].~PendingBuffer();
567 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
572 void ContextImpl::destroy()
574 if(mRefs.load() != 0)
575 throw std::runtime_error("Context is in use");
576 if(!mBuffers.empty())
577 throw std::runtime_error("Trying to destroy a context with buffers");
579 if(mThread.joinable())
581 std::unique_lock<std::mutex> lock(mWakeMutex);
582 mQuitThread.store(true, std::memory_order_release);
583 lock.unlock();
584 mWakeThread.notify_all();
585 mThread.join();
588 alcDestroyContext(mContext);
589 mContext = nullptr;
591 mDevice->removeContext(this);
595 void ContextImpl::startBatch()
597 alcSuspendContext(mContext);
598 mIsBatching = true;
601 void ContextImpl::endBatch()
603 alcProcessContext(mContext);
604 mIsBatching = false;
608 SharedPtr<MessageHandler> ContextImpl::setMessageHandler(SharedPtr<MessageHandler> handler)
610 std::lock_guard<std::mutex> lock(mContextMutex);
611 mMessage.swap(handler);
612 return handler;
616 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds msec)
618 if(msec.count() < 0 || msec > std::chrono::milliseconds(1000))
619 throw std::runtime_error("Async wake interval out of range");
620 mWakeInterval.store(msec);
621 mWakeMutex.lock(); mWakeMutex.unlock();
622 mWakeThread.notify_all();
626 DecoderOrExceptT ContextImpl::findDecoder(const String &name)
628 DecoderOrExceptT ret;
630 CheckContext(this);
631 auto file = FileIOFactory::get().openFile(name);
632 if(file) return (ret = GetDecoder(name, std::move(file)));
634 // Resource not found. Try to find a substitute.
635 if(!mMessage.get()) return (ret = std::runtime_error("Failed to open "+name));
636 String oldname = name;
637 do {
638 String newname(mMessage->resourceNotFound(oldname));
639 if(newname.empty())
640 return (ret = std::runtime_error("Failed to open "+oldname));
641 file = FileIOFactory::get().openFile(newname);
642 oldname = std::move(newname);
643 } while(!file);
645 return (ret = GetDecoder(oldname, std::move(file)));
648 SharedPtr<Decoder> ContextImpl::createDecoder(const String &name)
650 CheckContext(this);
651 DecoderOrExceptT dec = findDecoder(name);
652 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
653 return *decoder;
654 throw std::get<std::runtime_error>(dec);
658 bool ContextImpl::isSupported(ChannelConfig channels, SampleType type) const
660 CheckContext(this);
661 return GetFormat(channels, type) != AL_NONE;
665 ArrayView<String> ContextImpl::getAvailableResamplers()
667 CheckContext(this);
668 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
670 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
671 mResamplers.reserve(num_resamplers);
672 for(int i = 0;i < num_resamplers;i++)
673 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
674 if(mResamplers.empty())
675 mResamplers.emplace_back();
677 return mResamplers;
680 ALsizei ContextImpl::getDefaultResamplerIndex() const
682 CheckContext(this);
683 if(!hasExtension(SOFT_source_resampler))
684 return 0;
685 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
689 BufferOrExceptT ContextImpl::doCreateBuffer(const String &name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
691 BufferOrExceptT retval;
692 ALuint srate = decoder->getFrequency();
693 ChannelConfig chans = decoder->getChannelConfig();
694 SampleType type = decoder->getSampleType();
695 ALuint frames = decoder->getLength();
697 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
698 frames = decoder->read(data.data(), frames);
699 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
700 data.resize(FramesToBytes(frames, chans, type));
702 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
703 if(loop_pts.first >= loop_pts.second)
704 loop_pts = std::make_pair(0, frames);
705 else
707 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
708 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
711 // Get the format before calling the bufferLoading message handler, to
712 // ensure it's something OpenAL can handle.
713 ALenum format = GetFormat(chans, type);
714 if(format == AL_NONE)
716 std::stringstream sstr;
717 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
718 return (retval = std::runtime_error(sstr.str()));
721 if(mMessage.get())
722 mMessage->bufferLoading(name, chans, type, srate, data);
724 alGetError();
725 ALuint bid = 0;
726 alGenBuffers(1, &bid);
727 alBufferData(bid, format, data.data(), data.size(), srate);
728 if(hasExtension(SOFT_loop_points))
730 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
731 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
733 if(alGetError() != AL_NO_ERROR)
735 alDeleteBuffers(1, &bid);
736 return (retval = std::runtime_error("Failed to buffer data"));
739 return (retval = mBuffers.insert(iter,
740 MakeUnique<BufferImpl>(this, bid, srate, chans, type, true, name)
741 )->get());
744 BufferOrExceptT ContextImpl::doCreateBufferAsync(const String &name, Vector<UniquePtr<BufferImpl>>::iterator iter, SharedPtr<Decoder> decoder)
746 BufferOrExceptT retval;
747 ALuint srate = decoder->getFrequency();
748 ChannelConfig chans = decoder->getChannelConfig();
749 SampleType type = decoder->getSampleType();
750 ALuint frames = decoder->getLength();
751 if(!frames) return (retval = std::runtime_error("No samples for buffer"));
753 ALenum format = GetFormat(chans, type);
754 if(format == AL_NONE)
756 std::stringstream sstr;
757 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
758 return (retval = std::runtime_error(sstr.str()));
761 alGetError();
762 ALuint bid = 0;
763 alGenBuffers(1, &bid);
764 if(alGetError() != AL_NO_ERROR)
765 return (retval = std::runtime_error("Failed to create buffer"));
767 auto buffer = MakeUnique<BufferImpl>(this, bid, srate, chans, type, false, name);
769 if(mThread.get_id() == std::thread::id())
770 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
772 while(mPendingBuffers.write_space() == 0)
774 mWakeThread.notify_all();
775 std::this_thread::yield();
778 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
779 new(ringdata.buf) PendingBuffer{name, buffer.get(), decoder, format, frames};
780 mPendingBuffers.write_advance(1);
782 return (retval = mBuffers.insert(iter, std::move(buffer))->get());
785 Buffer ContextImpl::getBuffer(const String &name)
787 CheckContext(this);
789 auto hasher = std::hash<String>();
790 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
791 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
792 { return hasher(lhs->getName()) < rhs; }
794 if(iter != mBuffers.end() && (*iter)->getName() == name)
796 // Ensure the buffer is loaded before returning. getBuffer guarantees
797 // the returned buffer is loaded.
798 BufferImpl *buffer = iter->get();
799 while(buffer->getLoadStatus() == BufferLoadStatus::Pending)
800 std::this_thread::yield();
801 return Buffer(buffer);
804 BufferOrExceptT ret = doCreateBuffer(name, iter, createDecoder(name));
805 Buffer *buffer = std::get_if<Buffer>(&ret);
806 if(EXPECT(!buffer, false))
807 throw std::get<std::runtime_error>(ret);
808 return *buffer;
811 Buffer ContextImpl::getBufferAsync(const String &name)
813 CheckContext(this);
815 auto hasher = std::hash<String>();
816 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
817 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
818 { return hasher(lhs->getName()) < rhs; }
820 if(iter != mBuffers.end() && (*iter)->getName() == name)
821 return Buffer(iter->get());
823 BufferOrExceptT ret = doCreateBufferAsync(name, iter, createDecoder(name));
824 Buffer *buffer = std::get_if<Buffer>(&ret);
825 if(EXPECT(!buffer, false))
826 throw std::get<std::runtime_error>(ret);
827 mWakeMutex.lock(); mWakeMutex.unlock();
828 mWakeThread.notify_all();
829 return *buffer;
832 void ContextImpl::precacheBuffersAsync(ArrayView<String> names)
834 CheckContext(this);
836 auto hasher = std::hash<String>();
837 for(const String &name : names)
839 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
840 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
841 { return hasher(lhs->getName()) < rhs; }
843 if(iter != mBuffers.end() && (*iter)->getName() == name)
844 continue;
846 DecoderOrExceptT dec = findDecoder(name);
847 if(SharedPtr<Decoder> *decoder = std::get_if<SharedPtr<Decoder>>(&dec))
848 doCreateBufferAsync(name, iter, *decoder);
850 mWakeMutex.lock(); mWakeMutex.unlock();
851 mWakeThread.notify_all();
854 Buffer ContextImpl::createBufferFrom(const String &name, SharedPtr<Decoder> decoder)
856 CheckContext(this);
858 auto hasher = std::hash<String>();
859 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
860 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
861 { return hasher(lhs->getName()) < rhs; }
863 if(iter != mBuffers.end() && (*iter)->getName() == name)
864 throw std::runtime_error("Buffer \""+name+"\" already exists");
866 BufferOrExceptT ret = doCreateBuffer(name, iter, std::move(decoder));
867 Buffer *buffer = std::get_if<Buffer>(&ret);
868 if(EXPECT(!buffer, false))
869 throw std::get<std::runtime_error>(ret);
870 return *buffer;
873 Buffer ContextImpl::createBufferAsyncFrom(const String &name, SharedPtr<Decoder> decoder)
875 CheckContext(this);
877 auto hasher = std::hash<String>();
878 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
879 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
880 { return hasher(lhs->getName()) < rhs; }
882 if(iter != mBuffers.end() && (*iter)->getName() == name)
883 throw std::runtime_error("Buffer \""+name+"\" already exists");
885 BufferOrExceptT ret = doCreateBufferAsync(name, iter, std::move(decoder));
886 Buffer *buffer = std::get_if<Buffer>(&ret);
887 if(EXPECT(!buffer, false))
888 throw std::get<std::runtime_error>(ret);
889 mWakeMutex.lock(); mWakeMutex.unlock();
890 mWakeThread.notify_all();
891 return *buffer;
895 void ContextImpl::removeBuffer(const String &name)
897 CheckContext(this);
898 auto hasher = std::hash<String>();
899 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
900 [hasher](const UniquePtr<BufferImpl> &lhs, size_t rhs) -> bool
901 { return hasher(lhs->getName()) < rhs; }
903 if(iter != mBuffers.end() && (*iter)->getName() == name)
905 (*iter)->cleanup();
906 mBuffers.erase(iter);
911 ALuint ContextImpl::getSourceId(ALuint maxprio)
913 ALuint id = 0;
914 if(mSourceIds.empty())
916 alGetError();
917 alGenSources(1, &id);
918 if(alGetError() == AL_NO_ERROR)
919 return id;
921 SourceImpl *lowest = nullptr;
922 for(SourceBufferUpdateEntry &entry : mPlaySources)
924 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
925 lowest = entry.mSource;
927 for(SourceStreamUpdateEntry &entry : mStreamSources)
929 if(!lowest || entry.mSource->getPriority() < lowest->getPriority())
930 lowest = entry.mSource;
932 if(lowest && lowest->getPriority() < maxprio)
934 lowest->stop();
935 if(mMessage.get())
936 mMessage->sourceForceStopped(lowest);
939 if(mSourceIds.empty())
940 throw std::runtime_error("No available sources");
942 id = mSourceIds.top();
943 mSourceIds.pop();
944 return id;
948 Source ContextImpl::createSource()
950 CheckContext(this);
952 SourceImpl *source;
953 if(!mFreeSources.empty())
955 source = mFreeSources.front();
956 mFreeSources.pop();
958 else
960 mAllSources.emplace_back(this);
961 source = &mAllSources.back();
963 return Source(source);
967 void ContextImpl::addPlayingSource(SourceImpl *source, ALuint id)
969 auto iter = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
970 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
971 { return lhs.mSource < rhs; }
973 if(iter == mPlaySources.end() || iter->mSource != source)
974 mPlaySources.insert(iter, {source,id});
977 void ContextImpl::addPlayingSource(SourceImpl *source)
979 auto iter = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
980 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
981 { return lhs.mSource < rhs; }
983 if(iter == mStreamSources.end() || iter->mSource != source)
984 mStreamSources.insert(iter, {source});
987 void ContextImpl::removePlayingSource(SourceImpl *source)
989 auto iter0 = std::lower_bound(mPlaySources.begin(), mPlaySources.end(), source,
990 [](const SourceBufferUpdateEntry &lhs, SourceImpl *rhs) -> bool
991 { return lhs.mSource < rhs; }
993 if(iter0 != mPlaySources.end() && iter0->mSource == source)
994 mPlaySources.erase(iter0);
995 else
997 auto iter1 = std::lower_bound(mStreamSources.begin(), mStreamSources.end(), source,
998 [](const SourceStreamUpdateEntry &lhs, SourceImpl *rhs) -> bool
999 { return lhs.mSource < rhs; }
1001 if(iter1 != mStreamSources.end() && iter1->mSource == source)
1002 mStreamSources.erase(iter1);
1007 void ContextImpl::addStream(SourceImpl *source)
1009 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1010 if(mThread.get_id() == std::thread::id())
1011 mThread = std::thread(std::mem_fn(&ContextImpl::backgroundProc), this);
1012 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1013 if(iter == mStreamingSources.end() || *iter != source)
1014 mStreamingSources.insert(iter, source);
1017 void ContextImpl::removeStream(SourceImpl *source)
1019 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
1020 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1021 if(iter != mStreamingSources.end() && *iter == source)
1022 mStreamingSources.erase(iter);
1025 void ContextImpl::removeStreamNoLock(SourceImpl *source)
1027 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
1028 if(iter != mStreamingSources.end() && *iter == source)
1029 mStreamingSources.erase(iter);
1033 AuxiliaryEffectSlot ContextImpl::createAuxiliaryEffectSlot()
1035 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
1036 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1037 CheckContext(this);
1039 alGetError();
1040 ALuint id = 0;
1041 alGenAuxiliaryEffectSlots(1, &id);
1042 if(alGetError() != AL_NO_ERROR)
1043 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
1044 try {
1045 return AuxiliaryEffectSlot(new AuxiliaryEffectSlotImpl(this, id));
1047 catch(...) {
1048 alDeleteAuxiliaryEffectSlots(1, &id);
1049 throw;
1054 Effect ContextImpl::createEffect()
1056 if(!hasExtension(EXT_EFX))
1057 throw std::runtime_error("Effects not supported");
1058 CheckContext(this);
1060 alGetError();
1061 ALuint id = 0;
1062 alGenEffects(1, &id);
1063 if(alGetError() != AL_NO_ERROR)
1064 throw std::runtime_error("Failed to create Effect");
1065 try {
1066 return Effect(new EffectImpl(this, id));
1068 catch(...) {
1069 alDeleteEffects(1, &id);
1070 throw;
1075 SourceGroup ContextImpl::createSourceGroup(String name)
1077 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1078 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1079 { return lhs->getName() < rhs; }
1081 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
1082 throw std::runtime_error("Duplicate source group name");
1083 iter = mSourceGroups.insert(iter, MakeUnique<SourceGroupImpl>(this, std::move(name)));
1084 return SourceGroup(iter->get());
1087 SourceGroup ContextImpl::getSourceGroup(const String &name)
1089 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
1090 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1091 { return lhs->getName() < rhs; }
1093 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1094 throw std::runtime_error("Source group not found");
1095 return SourceGroup(iter->get());
1098 void ContextImpl::freeSourceGroup(SourceGroupImpl *group)
1100 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1101 [](const UniquePtr<SourceGroupImpl> &lhs, const String &rhs) -> bool
1102 { return lhs->getName() < rhs; }
1104 if(iter != mSourceGroups.end() && iter->get() == group)
1105 mSourceGroups.erase(iter);
1109 void ContextImpl::setDopplerFactor(ALfloat factor)
1111 if(!(factor >= 0.0f))
1112 throw std::runtime_error("Doppler factor out of range");
1113 CheckContext(this);
1114 alDopplerFactor(factor);
1118 void ContextImpl::setSpeedOfSound(ALfloat speed)
1120 if(!(speed > 0.0f))
1121 throw std::runtime_error("Speed of sound out of range");
1122 CheckContext(this);
1123 alSpeedOfSound(speed);
1127 void ContextImpl::setDistanceModel(DistanceModel model)
1129 CheckContext(this);
1130 alDistanceModel((ALenum)model);
1134 void ContextImpl::update()
1136 CheckContext(this);
1137 mPlaySources.erase(
1138 std::remove_if(mPlaySources.begin(), mPlaySources.end(),
1139 [](const SourceBufferUpdateEntry &entry) -> bool
1140 { return !entry.mSource->playUpdate(entry.mId); }
1141 ), mPlaySources.end()
1143 mStreamSources.erase(
1144 std::remove_if(mStreamSources.begin(), mStreamSources.end(),
1145 [](const SourceStreamUpdateEntry &entry) -> bool
1146 { return !entry.mSource->playUpdate(); }
1147 ), mStreamSources.end()
1149 if(!mWakeInterval.load(std::memory_order_relaxed).count())
1151 // For performance reasons, don't wait for the thread's mutex. This
1152 // should be called often enough to keep up with any and all streams
1153 // regardless.
1154 mWakeThread.notify_all();
1157 if(hasExtension(EXT_disconnect) && mIsConnected)
1159 ALCint connected;
1160 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1161 mIsConnected = connected;
1162 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1166 void Context::destroy()
1168 pImpl->destroy();
1169 pImpl = nullptr;
1171 DECL_THUNK0(Device, Context, getDevice,)
1172 DECL_THUNK0(void, Context, startBatch,)
1173 DECL_THUNK0(void, Context, endBatch,)
1174 DECL_THUNK0(Listener, Context, getListener,)
1175 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1176 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1177 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1178 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1179 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, const String&)
1180 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1181 DECL_THUNK0(ArrayView<String>, Context, getAvailableResamplers,)
1182 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1183 DECL_THUNK1(Buffer, Context, getBuffer,, const String&)
1184 DECL_THUNK1(Buffer, Context, getBufferAsync,, const String&)
1185 DECL_THUNK1(void, Context, precacheBuffersAsync,, ArrayView<String>)
1186 DECL_THUNK2(Buffer, Context, createBufferFrom,, const String&, SharedPtr<Decoder>)
1187 DECL_THUNK2(Buffer, Context, createBufferAsyncFrom,, const String&, SharedPtr<Decoder>)
1188 DECL_THUNK1(void, Context, removeBuffer,, const String&)
1189 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1190 DECL_THUNK0(Source, Context, createSource,)
1191 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1192 DECL_THUNK0(Effect, Context, createEffect,)
1193 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, String)
1194 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, const String&)
1195 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1196 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1197 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1198 DECL_THUNK0(void, Context, update,)
1201 void Context::MakeCurrent(Context context)
1202 { ContextImpl::MakeCurrent(context.pImpl); }
1204 Context Context::GetCurrent()
1205 { return Context(ContextImpl::GetCurrent()); }
1207 void Context::MakeThreadCurrent(Context context)
1208 { ContextImpl::MakeThreadCurrent(context.pImpl); }
1210 Context Context::GetThreadCurrent()
1211 { return Context(ContextImpl::GetThreadCurrent()); }
1214 void ListenerImpl::setGain(ALfloat gain)
1216 if(!(gain >= 0.0f))
1217 throw std::runtime_error("Gain out of range");
1218 CheckContext(mContext);
1219 alListenerf(AL_GAIN, gain);
1223 void ListenerImpl::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1225 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1226 CheckContext(mContext);
1227 Batcher batcher = mContext->getBatcher();
1228 alListenerfv(AL_POSITION, position.getPtr());
1229 alListenerfv(AL_VELOCITY, velocity.getPtr());
1230 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1233 void ListenerImpl::setPosition(ALfloat x, ALfloat y, ALfloat z)
1235 CheckContext(mContext);
1236 alListener3f(AL_POSITION, x, y, z);
1239 void ListenerImpl::setPosition(const ALfloat *pos)
1241 CheckContext(mContext);
1242 alListenerfv(AL_POSITION, pos);
1245 void ListenerImpl::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1247 CheckContext(mContext);
1248 alListener3f(AL_VELOCITY, x, y, z);
1251 void ListenerImpl::setVelocity(const ALfloat *vel)
1253 CheckContext(mContext);
1254 alListenerfv(AL_VELOCITY, vel);
1257 void ListenerImpl::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1259 CheckContext(mContext);
1260 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1261 alListenerfv(AL_ORIENTATION, ori);
1264 void ListenerImpl::setOrientation(const ALfloat *at, const ALfloat *up)
1266 CheckContext(mContext);
1267 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1268 alListenerfv(AL_ORIENTATION, ori);
1271 void ListenerImpl::setOrientation(const ALfloat *ori)
1273 CheckContext(mContext);
1274 alListenerfv(AL_ORIENTATION, ori);
1277 void ListenerImpl::setMetersPerUnit(ALfloat m_u)
1279 if(!(m_u > 0.0f))
1280 throw std::runtime_error("Invalid meters per unit");
1281 CheckContext(mContext);
1282 if(mContext->hasExtension(EXT_EFX))
1283 alListenerf(AL_METERS_PER_UNIT, m_u);
1287 using Vector3Pair = std::pair<Vector3,Vector3>;
1289 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1290 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1291 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1292 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1293 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1294 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1295 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1296 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1297 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1298 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)