Use an alias for static-sized array types
[alure.git] / src / context.cpp
blob62cb6e4965410e381b412e1334e5b80911a75f2d
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 Buffer ALContext::doCreateBuffer(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
672 ALuint srate = decoder->getFrequency();
673 ChannelConfig chans = decoder->getChannelConfig();
674 SampleType type = decoder->getSampleType();
675 ALuint frames = decoder->getLength();
677 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
678 frames = decoder->read(&data[0], frames);
679 if(!frames) throw std::runtime_error("No samples for buffer");
680 data.resize(FramesToBytes(frames, chans, type));
682 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
683 if(loop_pts.first >= loop_pts.second)
684 loop_pts = std::make_pair(0, frames);
685 else
687 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
688 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
691 // Get the format before calling the bufferLoading message handler, to
692 // ensure it's something OpenAL can handle.
693 ALenum format = GetFormat(chans, type);
694 if(format == AL_NONE)
696 std::stringstream sstr;
697 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
698 throw std::runtime_error(sstr.str());
701 if(mMessage.get())
702 mMessage->bufferLoading(name, chans, type, srate, data);
704 alGetError();
705 ALuint bid = 0;
706 try {
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)
715 throw std::runtime_error("Failed to buffer data");
717 return Buffer(mBuffers.insert(iter,
718 MakeUnique<ALBuffer>(this, bid, srate, chans, type, true, name)
719 )->get());
721 catch(...) {
722 alDeleteBuffers(1, &bid);
723 throw;
727 Buffer ALContext::doCreateBufferAsync(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
729 ALuint srate = decoder->getFrequency();
730 ChannelConfig chans = decoder->getChannelConfig();
731 SampleType type = decoder->getSampleType();
732 ALuint frames = decoder->getLength();
733 if(!frames) throw std::runtime_error("No samples for buffer");
735 ALenum format = GetFormat(chans, type);
736 if(format == AL_NONE)
738 std::stringstream sstr;
739 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
740 throw std::runtime_error(sstr.str());
743 alGetError();
744 ALuint bid = 0;
745 alGenBuffers(1, &bid);
746 if(alGetError() != AL_NO_ERROR)
747 throw std::runtime_error("Failed to buffer data");
749 auto buffer = MakeUnique<ALBuffer>(this, bid, srate, chans, type, false, name);
751 if(mThread.get_id() == std::thread::id())
752 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
754 while(mPendingBuffers.write_space() == 0)
755 std::this_thread::yield();
757 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
758 new(ringdata.buf) PendingBuffer{name, buffer.get(), decoder, format, frames};
759 mPendingBuffers.write_advance(1);
760 mWakeMutex.lock(); mWakeMutex.unlock();
761 mWakeThread.notify_all();
763 return Buffer(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 return doCreateBuffer(name, iter, createDecoder(name));
788 Buffer ALContext::getBufferAsync(const String &name)
790 CheckContext(this);
792 auto hasher = std::hash<String>();
793 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
794 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
795 { return hasher(lhs->getName()) < rhs; }
797 if(iter != mBuffers.end() && (*iter)->getName() == name)
798 return Buffer(iter->get());
800 return doCreateBufferAsync(name, iter, createDecoder(name));
803 Buffer ALContext::createBufferFrom(const String &name, SharedPtr<Decoder> decoder)
805 CheckContext(this);
807 auto hasher = std::hash<String>();
808 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
809 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
810 { return hasher(lhs->getName()) < rhs; }
812 if(iter != mBuffers.end() && (*iter)->getName() == name)
813 throw std::runtime_error("Buffer \""+name+"\" already exists");
815 return doCreateBuffer(name, iter, std::move(decoder));
818 Buffer ALContext::createBufferAsyncFrom(const String &name, SharedPtr<Decoder> decoder)
820 CheckContext(this);
822 auto hasher = std::hash<String>();
823 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
824 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
825 { return hasher(lhs->getName()) < rhs; }
827 if(iter != mBuffers.end() && (*iter)->getName() == name)
828 throw std::runtime_error("Buffer \""+name+"\" already exists");
830 return doCreateBufferAsync(name, iter, std::move(decoder));
834 void ALContext::removeBuffer(const String &name)
836 CheckContext(this);
837 auto hasher = std::hash<String>();
838 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
839 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
840 { return hasher(lhs->getName()) < rhs; }
842 if(iter != mBuffers.end() && (*iter)->getName() == name)
844 (*iter)->cleanup();
845 mBuffers.erase(iter);
850 ALuint ALContext::getSourceId(ALuint maxprio)
852 CheckContext(this);
854 ALuint id = 0;
855 if(mSourceIds.empty())
857 alGetError();
858 alGenSources(1, &id);
859 if(alGetError() == AL_NO_ERROR)
860 return id;
862 ALSource *lowest = nullptr;
863 for(ALSource *src : mUsedSources)
865 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
866 lowest = src;
868 if(lowest && lowest->getPriority() < maxprio)
870 lowest->makeStopped();
871 if(mMessage.get())
872 mMessage->sourceForceStopped(lowest);
875 if(mSourceIds.empty())
876 throw std::runtime_error("No available sources");
878 id = mSourceIds.top();
879 mSourceIds.pop();
880 return id;
884 Source ALContext::createSource()
886 CheckContext(this);
888 ALSource *source;
889 if(!mFreeSources.empty())
891 source = mFreeSources.front();
892 mFreeSources.pop();
894 else
896 mAllSources.emplace_back(this);
897 source = &mAllSources.back();
899 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
900 if(iter == mUsedSources.end() || *iter != source)
901 mUsedSources.insert(iter, source);
902 return Source(source);
905 void ALContext::freeSource(ALSource *source)
907 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
908 if(iter != mUsedSources.end() && *iter == source) mUsedSources.erase(iter);
909 mFreeSources.push(source);
913 void ALContext::addStream(ALSource *source)
915 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
916 if(mThread.get_id() == std::thread::id())
917 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
918 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
919 if(iter == mStreamingSources.end() || *iter != source)
920 mStreamingSources.insert(iter, source);
923 void ALContext::removeStream(ALSource *source)
925 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
926 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
927 if(iter != mStreamingSources.end() && *iter == source)
928 mStreamingSources.erase(iter);
931 void ALContext::removeStreamNoLock(ALSource *source)
933 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
934 if(iter != mStreamingSources.end() && *iter == source)
935 mStreamingSources.erase(iter);
939 AuxiliaryEffectSlot ALContext::createAuxiliaryEffectSlot()
941 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
942 throw std::runtime_error("AuxiliaryEffectSlots not supported");
943 CheckContext(this);
945 alGetError();
946 ALuint id = 0;
947 alGenAuxiliaryEffectSlots(1, &id);
948 if(alGetError() != AL_NO_ERROR)
949 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
950 try {
951 return AuxiliaryEffectSlot(new ALAuxiliaryEffectSlot(this, id));
953 catch(...) {
954 alDeleteAuxiliaryEffectSlots(1, &id);
955 throw;
960 Effect ALContext::createEffect()
962 if(!hasExtension(EXT_EFX))
963 throw std::runtime_error("Effects not supported");
964 CheckContext(this);
966 alGetError();
967 ALuint id = 0;
968 alGenEffects(1, &id);
969 if(alGetError() != AL_NO_ERROR)
970 throw std::runtime_error("Failed to create Effect");
971 try {
972 return Effect(new ALEffect(this, id));
974 catch(...) {
975 alDeleteEffects(1, &id);
976 throw;
981 SourceGroup ALContext::createSourceGroup(String name)
983 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
984 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
985 { return lhs->getName() < rhs; }
987 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
988 throw std::runtime_error("Duplicate source group name");
989 iter = mSourceGroups.insert(iter, MakeUnique<ALSourceGroup>(this, std::move(name)));
990 return SourceGroup(iter->get());
993 SourceGroup ALContext::getSourceGroup(const String &name)
995 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
996 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
997 { return lhs->getName() < rhs; }
999 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
1000 throw std::runtime_error("Source group not found");
1001 return SourceGroup(iter->get());
1004 void ALContext::freeSourceGroup(ALSourceGroup *group)
1006 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
1007 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
1008 { return lhs->getName() < rhs; }
1010 if(iter != mSourceGroups.end() && iter->get() == group)
1011 mSourceGroups.erase(iter);
1015 void ALContext::setDopplerFactor(ALfloat factor)
1017 if(!(factor >= 0.0f))
1018 throw std::runtime_error("Doppler factor out of range");
1019 CheckContext(this);
1020 alDopplerFactor(factor);
1024 void ALContext::setSpeedOfSound(ALfloat speed)
1026 if(!(speed > 0.0f))
1027 throw std::runtime_error("Speed of sound out of range");
1028 CheckContext(this);
1029 alSpeedOfSound(speed);
1033 void ALContext::setDistanceModel(DistanceModel model)
1035 CheckContext(this);
1036 alDistanceModel((ALenum)model);
1040 void ALContext::update()
1042 CheckContext(this);
1043 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fn(&ALSource::updateNoCtxCheck));
1044 if(!mWakeInterval.load().count())
1046 // For performance reasons, don't wait for the thread's mutex. This
1047 // should be called often enough to keep up with any and all streams
1048 // regardless.
1049 mWakeThread.notify_all();
1052 if(hasExtension(EXT_disconnect) && mIsConnected)
1054 ALCint connected;
1055 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
1056 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
1057 mIsConnected = connected;
1061 void Context::destroy()
1063 pImpl->destroy();
1064 pImpl = nullptr;
1066 DECL_THUNK0(Device, Context, getDevice,)
1067 DECL_THUNK0(void, Context, startBatch,)
1068 DECL_THUNK0(void, Context, endBatch,)
1069 DECL_THUNK0(Listener, Context, getListener,)
1070 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
1071 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
1072 DECL_THUNK1(void, Context, setAsyncWakeInterval,, std::chrono::milliseconds)
1073 DECL_THUNK0(std::chrono::milliseconds, Context, getAsyncWakeInterval, const)
1074 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, const String&)
1075 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
1076 DECL_THUNK0(const Vector<String>&, Context, getAvailableResamplers,)
1077 DECL_THUNK0(ALsizei, Context, getDefaultResamplerIndex, const)
1078 DECL_THUNK1(Buffer, Context, getBuffer,, const String&)
1079 DECL_THUNK1(Buffer, Context, getBufferAsync,, const String&)
1080 DECL_THUNK2(Buffer, Context, createBufferFrom,, const String&, SharedPtr<Decoder>)
1081 DECL_THUNK2(Buffer, Context, createBufferAsyncFrom,, const String&, SharedPtr<Decoder>)
1082 DECL_THUNK1(void, Context, removeBuffer,, const String&)
1083 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
1084 DECL_THUNK0(Source, Context, createSource,)
1085 DECL_THUNK0(AuxiliaryEffectSlot, Context, createAuxiliaryEffectSlot,)
1086 DECL_THUNK0(Effect, Context, createEffect,)
1087 DECL_THUNK1(SourceGroup, Context, createSourceGroup,, String)
1088 DECL_THUNK1(SourceGroup, Context, getSourceGroup,, const String&)
1089 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
1090 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
1091 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
1092 DECL_THUNK0(void, Context, update,)
1095 void Context::MakeCurrent(Context context)
1096 { ALContext::MakeCurrent(context.pImpl); }
1098 Context Context::GetCurrent()
1099 { return Context(ALContext::GetCurrent()); }
1101 void Context::MakeThreadCurrent(Context context)
1102 { ALContext::MakeThreadCurrent(context.pImpl); }
1104 Context Context::GetThreadCurrent()
1105 { return Context(ALContext::GetThreadCurrent()); }
1108 void ALListener::setGain(ALfloat gain)
1110 if(!(gain >= 0.0f))
1111 throw std::runtime_error("Gain out of range");
1112 CheckContext(mContext);
1113 alListenerf(AL_GAIN, gain);
1117 void ALListener::set3DParameters(const Vector3 &position, const Vector3 &velocity, std::pair<Vector3,Vector3> orientation)
1119 static_assert(sizeof(orientation) == sizeof(ALfloat[6]), "Invalid Vector3 pair size");
1120 CheckContext(mContext);
1121 Batcher batcher = mContext->getBatcher();
1122 alListenerfv(AL_POSITION, position.getPtr());
1123 alListenerfv(AL_VELOCITY, velocity.getPtr());
1124 alListenerfv(AL_ORIENTATION, orientation.first.getPtr());
1127 void ALListener::setPosition(ALfloat x, ALfloat y, ALfloat z)
1129 CheckContext(mContext);
1130 alListener3f(AL_POSITION, x, y, z);
1133 void ALListener::setPosition(const ALfloat *pos)
1135 CheckContext(mContext);
1136 alListenerfv(AL_POSITION, pos);
1139 void ALListener::setVelocity(ALfloat x, ALfloat y, ALfloat z)
1141 CheckContext(mContext);
1142 alListener3f(AL_VELOCITY, x, y, z);
1145 void ALListener::setVelocity(const ALfloat *vel)
1147 CheckContext(mContext);
1148 alListenerfv(AL_VELOCITY, vel);
1151 void ALListener::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
1153 CheckContext(mContext);
1154 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
1155 alListenerfv(AL_ORIENTATION, ori);
1158 void ALListener::setOrientation(const ALfloat *at, const ALfloat *up)
1160 CheckContext(mContext);
1161 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
1162 alListenerfv(AL_ORIENTATION, ori);
1165 void ALListener::setOrientation(const ALfloat *ori)
1167 CheckContext(mContext);
1168 alListenerfv(AL_ORIENTATION, ori);
1171 void ALListener::setMetersPerUnit(ALfloat m_u)
1173 if(!(m_u > 0.0f))
1174 throw std::runtime_error("Invalid meters per unit");
1175 CheckContext(mContext);
1176 if(mContext->hasExtension(EXT_EFX))
1177 alListenerf(AL_METERS_PER_UNIT, m_u);
1181 using Vector3Pair = std::pair<Vector3,Vector3>;
1183 DECL_THUNK1(void, Listener, setGain,, ALfloat)
1184 DECL_THUNK3(void, Listener, set3DParameters,, const Vector3&, const Vector3&, Vector3Pair)
1185 DECL_THUNK3(void, Listener, setPosition,, ALfloat, ALfloat, ALfloat)
1186 DECL_THUNK1(void, Listener, setPosition,, const ALfloat*)
1187 DECL_THUNK3(void, Listener, setVelocity,, ALfloat, ALfloat, ALfloat)
1188 DECL_THUNK1(void, Listener, setVelocity,, const ALfloat*)
1189 DECL_THUNK6(void, Listener, setOrientation,, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat, ALfloat)
1190 DECL_THUNK2(void, Listener, setOrientation,, const ALfloat*, const ALfloat*)
1191 DECL_THUNK1(void, Listener, setOrientation,, const ALfloat*)
1192 DECL_THUNK1(void, Listener, setMetersPerUnit,, ALfloat)