Use a custom default istream on Windows
[alure.git] / src / context.cpp
blob42b3bfef6f4a5b0d942fbf04bdf0ccd259fb612e
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 std::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 static const std::pair<String,UniquePtr<DecoderFactory>> sDefaultDecoders[] = {
193 { "_alure_int_wave", MakeUnique<WaveDecoderFactory>() },
195 #ifdef HAVE_VORBISFILE
196 { "_alure_int_vorbis", MakeUnique<VorbisFileDecoderFactory>() },
197 #endif
198 #ifdef HAVE_LIBFLAC
199 { "_alure_int_flac", MakeUnique<FlacDecoderFactory>() },
200 #endif
201 #ifdef HAVE_OPUSFILE
202 { "_alure_int_opus", MakeUnique<OpusFileDecoderFactory>() },
203 #endif
204 #ifdef HAVE_LIBSNDFILE
205 { "_alure_int_sndfile", MakeUnique<SndFileDecoderFactory>() },
206 #endif
207 #ifdef HAVE_MPG123
208 { "_alure_int_mpg123", MakeUnique<Mpg123DecoderFactory>() },
209 #endif
212 static std::map<String,UniquePtr<DecoderFactory>> sDecoders;
215 template<typename T>
216 static SharedPtr<Decoder> GetDecoder(const String &name, UniquePtr<std::istream> &file, T start, T end)
218 while(start != end)
220 DecoderFactory *factory = start->second.get();
221 auto decoder = factory->createDecoder(file);
222 if(decoder) return decoder;
224 if(!file || !(file->clear(),file->seekg(0)))
225 throw std::runtime_error("Failed to rewind "+name+" for the next decoder factory");
227 ++start;
230 return nullptr;
233 static SharedPtr<Decoder> GetDecoder(const String &name, UniquePtr<std::istream> file)
235 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
236 if(!decoder) decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
237 if(!decoder) throw std::runtime_error("No decoder for "+name);
238 return decoder;
241 void RegisterDecoder(const String &name, UniquePtr<DecoderFactory> factory)
243 while(sDecoders.find(name) != sDecoders.end())
244 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
245 sDecoders.insert(std::make_pair(name, std::move(factory)));
248 UniquePtr<DecoderFactory> UnregisterDecoder(const String &name)
250 auto iter = sDecoders.find(name);
251 if(iter != sDecoders.end())
253 UniquePtr<DecoderFactory> factory = std::move(iter->second);
254 sDecoders.erase(iter);
255 return factory;
257 return nullptr;
261 class DefaultFileIOFactory : public FileIOFactory {
262 UniquePtr<std::istream> openFile(const String &name) override final
264 #ifdef _WIN32
265 auto file = MakeUnique<Stream>(name.c_str());
266 #else
267 auto file = MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
268 #endif
269 if(!file->is_open()) file = nullptr;
270 return std::move(file);
273 static DefaultFileIOFactory sDefaultFileFactory;
275 static UniquePtr<FileIOFactory> sFileFactory;
276 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
278 std::swap(sFileFactory, factory);
279 return factory;
282 FileIOFactory &FileIOFactory::get()
284 FileIOFactory *factory = sFileFactory.get();
285 if(factory) return *factory;
286 return sDefaultFileFactory;
290 // Default message handler methods are no-ops.
291 MessageHandler::~MessageHandler()
295 void MessageHandler::deviceDisconnected(Device)
299 void MessageHandler::sourceStopped(Source)
303 void MessageHandler::sourceForceStopped(Source)
307 void MessageHandler::bufferLoading(const String&, ChannelConfig, SampleType, ALuint, const Vector<ALbyte>&)
311 String MessageHandler::resourceNotFound(const String&)
313 return String();
317 template<typename T>
318 static inline void LoadALFunc(T **func, const char *name)
319 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
321 static void LoadNothing(ALContext*) { }
323 static void LoadEFX(ALContext *ctx)
325 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
326 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
327 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
328 LoadALFunc(&ctx->alEffecti, "alEffecti");
329 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
330 LoadALFunc(&ctx->alEffectf, "alEffectf");
331 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
332 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
333 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
334 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
335 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
337 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
338 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
339 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
340 LoadALFunc(&ctx->alFilteri, "alFilteri");
341 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
342 LoadALFunc(&ctx->alFilterf, "alFilterf");
343 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
344 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
345 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
346 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
347 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
349 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
350 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
351 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
352 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
353 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
354 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
355 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
356 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
357 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
358 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
359 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
362 static void LoadSourceResampler(ALContext *ctx)
364 LoadALFunc(&ctx->alGetStringiSOFT, "alGetStringiSOFT");
367 static void LoadSourceLatency(ALContext *ctx)
369 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
370 LoadALFunc(&ctx->alGetSourcedvSOFT, "alGetSourcedvSOFT");
373 static const struct {
374 enum ALExtension extension;
375 const char name[32];
376 void (&loader)(ALContext*);
377 } ALExtensionList[] = {
378 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
380 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
381 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
382 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
384 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
385 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
386 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
388 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
389 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
390 { SOFT_source_resampler, "AL_SOFT_source_resampler", LoadSourceResampler },
391 { SOFT_source_spatialize, "AL_SOFT_source_spatialize", LoadNothing },
393 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
395 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
396 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
400 ALContext *ALContext::sCurrentCtx = nullptr;
401 thread_local ALContext *ALContext::sThreadCurrentCtx = nullptr;
403 void ALContext::MakeCurrent(ALContext *context)
405 std::unique_lock<std::mutex> lock1, lock2;
406 if(sCurrentCtx)
407 lock1 = std::unique_lock<std::mutex>(sCurrentCtx->mContextMutex);
408 if(context && context != sCurrentCtx)
409 lock2 = std::unique_lock<std::mutex>(context->mContextMutex);
411 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
412 throw std::runtime_error("Call to alcMakeContextCurrent failed");
413 if(context)
415 context->addRef();
416 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
418 std::swap(sCurrentCtx, context);
419 if(context) context->decRef();
421 if(sThreadCurrentCtx)
422 sThreadCurrentCtx->decRef();
423 sThreadCurrentCtx = 0;
425 if(sCurrentCtx && sCurrentCtx != context)
427 lock2.unlock();
428 sCurrentCtx->mWakeThread.notify_all();
432 void ALContext::MakeThreadCurrent(ALContext *context)
434 if(!ALDeviceManager::SetThreadContext)
435 throw std::runtime_error("Thread-local contexts unsupported");
436 if(ALDeviceManager::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
437 throw std::runtime_error("Call to alcSetThreadContext failed");
438 if(context)
440 context->addRef();
441 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
443 if(sThreadCurrentCtx)
444 sThreadCurrentCtx->decRef();
445 sThreadCurrentCtx = context;
448 void ALContext::setupExts()
450 ALCdevice *device = mDevice->getDevice();
451 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
452 for(const auto &entry : ALExtensionList)
454 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
455 alcIsExtensionPresent(device, entry.name) :
456 alIsExtensionPresent(entry.name);
457 if(mHasExt[entry.extension]) entry.loader(this);
462 void ALContext::backgroundProc()
464 if(ALDeviceManager::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
465 ALDeviceManager::SetThreadContext(getContext());
467 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
468 std::chrono::milliseconds waketime(0);
469 std::unique_lock<std::mutex> ctxlock(mContextMutex);
470 while(!mQuitThread.load(std::memory_order_acquire))
473 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
474 auto source = mStreamingSources.begin();
475 while(source != mStreamingSources.end())
477 if(!(*source)->updateAsync())
478 source = mStreamingSources.erase(source);
479 else
480 ++source;
484 // Only do one pending buffer at a time. In case there's several large
485 // buffers to load, we still need to process streaming sources so they
486 // don't underrun.
487 RingBuffer::Data ringdata = mPendingBuffers.get_read_vector()[0];
488 if(ringdata.len > 0)
490 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata.buf);
491 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, pb->mName, this);
492 pb->~PendingBuffer();
493 mPendingBuffers.read_advance(1);
494 continue;
497 std::unique_lock<std::mutex> wakelock(mWakeMutex);
498 if(!mQuitThread.load(std::memory_order_acquire) && mPendingBuffers.read_space() == 0)
500 ctxlock.unlock();
502 std::chrono::milliseconds interval = mWakeInterval.load();
503 if(interval.count() == 0)
504 mWakeThread.wait(wakelock);
505 else
507 auto now = std::chrono::steady_clock::now() - basetime;
508 while((waketime - now).count() <= 0) waketime += interval;
509 mWakeThread.wait_until(wakelock, waketime + basetime);
511 wakelock.unlock();
513 ctxlock.lock();
514 while(!mQuitThread.load(std::memory_order_acquire) &&
515 alcGetCurrentContext() != getContext())
516 mWakeThread.wait(ctxlock);
519 ctxlock.unlock();
521 if(ALDeviceManager::SetThreadContext)
522 ALDeviceManager::SetThreadContext(nullptr);
526 ALContext::ALContext(ALCcontext *context, ALDevice *device)
527 : mListener(this), mContext(context), mDevice(device), mRefs(0),
528 mHasExt{false}, mPendingBuffers(16, sizeof(PendingBuffer)),
529 mWakeInterval(std::chrono::milliseconds::zero()), mQuitThread(false),
530 mIsConnected(true), mIsBatching(false),
531 alGetSourcei64vSOFT(0), alGetSourcedvSOFT(0),
532 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
533 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
534 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
535 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
536 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
537 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
538 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
539 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
540 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
544 ALContext::~ALContext()
546 auto ringdata = mPendingBuffers.get_read_vector();
547 if(ringdata[0].len > 0)
549 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
550 for(size_t i = 0;i < ringdata[0].len;i++)
551 pb[i].~PendingBuffer();
552 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
553 for(size_t i = 0;i < ringdata[1].len;i++)
554 pb[i].~PendingBuffer();
556 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
561 void ALContext::destroy()
563 if(mRefs.load() != 0)
564 throw std::runtime_error("Context is in use");
565 if(!mBuffers.empty())
566 throw std::runtime_error("Trying to destroy a context with buffers");
568 if(mThread.joinable())
570 std::unique_lock<std::mutex> lock(mWakeMutex);
571 mQuitThread.store(true, std::memory_order_release);
572 lock.unlock();
573 mWakeThread.notify_all();
574 mThread.join();
577 alcDestroyContext(mContext);
578 mContext = nullptr;
580 mDevice->removeContext(this);
584 void ALContext::startBatch()
586 alcSuspendContext(mContext);
587 mIsBatching = true;
590 void ALContext::endBatch()
592 alcProcessContext(mContext);
593 mIsBatching = false;
597 SharedPtr<MessageHandler> ALContext::setMessageHandler(SharedPtr<MessageHandler> handler)
599 std::lock_guard<std::mutex> lock(mContextMutex);
600 mMessage.swap(handler);
601 return handler;
605 void ALContext::setAsyncWakeInterval(std::chrono::milliseconds msec)
607 mWakeInterval.store(msec);
608 mWakeMutex.lock(); mWakeMutex.unlock();
609 mWakeThread.notify_all();
613 SharedPtr<Decoder> ALContext::createDecoder(const String &name)
615 CheckContext(this);
616 auto file = FileIOFactory::get().openFile(name);
617 if(file) return GetDecoder(name, std::move(file));
619 // Resource not found. Try to find a substitute.
620 if(!mMessage.get()) throw std::runtime_error("Failed to open "+name);
621 String oldname = name;
622 do {
623 String newname(mMessage->resourceNotFound(oldname));
624 if(newname.empty())
625 throw std::runtime_error("Failed to open "+oldname);
626 file = FileIOFactory::get().openFile(newname);
627 oldname = std::move(newname);
628 } while(!file);
630 return GetDecoder(oldname, std::move(file));
634 bool ALContext::isSupported(ChannelConfig channels, SampleType type) const
636 CheckContext(this);
637 return GetFormat(channels, type) != AL_NONE;
641 const Vector<String> &ALContext::getAvailableResamplers()
643 CheckContext(this);
644 if(mResamplers.empty() && hasExtension(SOFT_source_resampler))
646 ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
647 mResamplers.reserve(num_resamplers);
648 for(int i = 0;i < num_resamplers;i++)
649 mResamplers.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i));
650 if(mResamplers.empty())
651 mResamplers.emplace_back();
653 return mResamplers;
656 ALsizei ALContext::getDefaultResamplerIndex() const
658 CheckContext(this);
659 if(!hasExtension(SOFT_source_resampler))
660 return 0;
661 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
665 Buffer ALContext::doCreateBuffer(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
667 ALuint srate = decoder->getFrequency();
668 ChannelConfig chans = decoder->getChannelConfig();
669 SampleType type = decoder->getSampleType();
670 ALuint frames = decoder->getLength();
672 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
673 frames = decoder->read(&data[0], frames);
674 if(!frames) throw std::runtime_error("No samples for buffer");
675 data.resize(FramesToBytes(frames, chans, type));
677 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
678 if(loop_pts.first >= loop_pts.second)
679 loop_pts = std::make_pair(0, frames);
680 else
682 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
683 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
686 // Get the format before calling the bufferLoading message handler, to
687 // ensure it's something OpenAL can handle.
688 ALenum format = GetFormat(chans, type);
689 if(format == AL_NONE)
691 std::stringstream sstr;
692 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
693 throw std::runtime_error(sstr.str());
696 if(mMessage.get())
697 mMessage->bufferLoading(name, chans, type, srate, data);
699 alGetError();
700 ALuint bid = 0;
701 try {
702 alGenBuffers(1, &bid);
703 alBufferData(bid, format, &data[0], data.size(), srate);
704 if(hasExtension(SOFT_loop_points))
706 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
707 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
709 if(alGetError() != AL_NO_ERROR)
710 throw std::runtime_error("Failed to buffer data");
712 return Buffer(mBuffers.insert(iter,
713 MakeUnique<ALBuffer>(this, bid, srate, chans, type, true, name)
714 )->get());
716 catch(...) {
717 alDeleteBuffers(1, &bid);
718 throw;
722 Buffer ALContext::doCreateBufferAsync(const String &name, Vector<UniquePtr<ALBuffer>>::iterator iter, SharedPtr<Decoder> decoder)
724 ALuint srate = decoder->getFrequency();
725 ChannelConfig chans = decoder->getChannelConfig();
726 SampleType type = decoder->getSampleType();
727 ALuint frames = decoder->getLength();
728 if(!frames) throw std::runtime_error("No samples for buffer");
730 ALenum format = GetFormat(chans, type);
731 if(format == AL_NONE)
733 std::stringstream sstr;
734 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
735 throw std::runtime_error(sstr.str());
738 alGetError();
739 ALuint bid = 0;
740 alGenBuffers(1, &bid);
741 if(alGetError() != AL_NO_ERROR)
742 throw std::runtime_error("Failed to buffer data");
744 auto buffer = MakeUnique<ALBuffer>(this, bid, srate, chans, type, false, name);
746 if(mThread.get_id() == std::thread::id())
747 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
749 while(mPendingBuffers.write_space() == 0)
750 std::this_thread::yield();
752 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
753 new(ringdata.buf) PendingBuffer{name, buffer.get(), decoder, format, frames};
754 mPendingBuffers.write_advance(1);
755 mWakeMutex.lock(); mWakeMutex.unlock();
756 mWakeThread.notify_all();
758 return Buffer(mBuffers.insert(iter, std::move(buffer))->get());
761 Buffer ALContext::getBuffer(const String &name)
763 CheckContext(this);
765 auto hasher = std::hash<String>();
766 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
767 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
768 { return hasher(lhs->getName()) < rhs; }
770 if(iter != mBuffers.end() && (*iter)->getName() == name)
772 // Ensure the buffer is loaded before returning. getBuffer guarantees
773 // the returned buffer is loaded.
774 ALBuffer *buffer = iter->get();
775 while(buffer->getLoadStatus() == BufferLoadStatus::Pending)
776 std::this_thread::yield();
777 return Buffer(buffer);
780 auto decoder = createDecoder(name);
781 return doCreateBuffer(name, iter, std::move(decoder));
784 Buffer ALContext::getBufferAsync(const String &name)
786 CheckContext(this);
788 auto hasher = std::hash<String>();
789 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
790 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
791 { return hasher(lhs->getName()) < rhs; }
793 if(iter != mBuffers.end() && (*iter)->getName() == name)
794 return Buffer(iter->get());
795 // NOTE: 'iter' is used later to insert a new entry!
797 auto decoder = createDecoder(name);
798 return doCreateBufferAsync(name, iter, std::move(decoder));
801 Buffer ALContext::createBufferFrom(const String &name, SharedPtr<Decoder> decoder)
803 CheckContext(this);
805 auto hasher = std::hash<String>();
806 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
807 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
808 { return hasher(lhs->getName()) < rhs; }
810 if(iter != mBuffers.end() && (*iter)->getName() == name)
811 throw std::runtime_error("Buffer \""+name+"\" already exists");
812 // NOTE: 'iter' is used later to insert a new entry!
814 return doCreateBuffer(name, iter, std::move(decoder));
817 Buffer ALContext::createBufferAsyncFrom(const String &name, SharedPtr<Decoder> decoder)
819 CheckContext(this);
821 auto hasher = std::hash<String>();
822 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
823 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
824 { return hasher(lhs->getName()) < rhs; }
826 if(iter != mBuffers.end() && (*iter)->getName() == name)
827 throw std::runtime_error("Buffer \""+name+"\" already exists");
828 // NOTE: 'iter' is used later to insert a new entry!
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)