Use pImpl for Buffers
[alure.git] / src / context.cpp
blobd46079221caaecfd86b558f1fd0b15d198ee8ae8
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 namespace alure
47 static const std::pair<String,UniquePtr<DecoderFactory>> sDefaultDecoders[] = {
48 { "_alure_int_wave", MakeUnique<WaveDecoderFactory>() },
50 #ifdef HAVE_VORBISFILE
51 { "_alure_int_vorbis", MakeUnique<VorbisFileDecoderFactory>() },
52 #endif
53 #ifdef HAVE_LIBFLAC
54 { "_alure_int_flac", MakeUnique<FlacDecoderFactory>() },
55 #endif
56 #ifdef HAVE_OPUSFILE
57 { "_alure_int_opus", MakeUnique<OpusFileDecoderFactory>() },
58 #endif
59 #ifdef HAVE_LIBSNDFILE
60 { "_alure_int_sndfile", MakeUnique<SndFileDecoderFactory>() },
61 #endif
62 #ifdef HAVE_MPG123
63 { "_alure_int_mpg123", MakeUnique<Mpg123DecoderFactory>() },
64 #endif
67 static std::map<String,UniquePtr<DecoderFactory>> sDecoders;
70 template<typename T>
71 static SharedPtr<Decoder> GetDecoder(const String &name, UniquePtr<std::istream> &file, T start, T end)
73 while(start != end)
75 DecoderFactory *factory = start->second.get();
76 auto decoder = factory->createDecoder(file);
77 if(decoder) return decoder;
79 if(!file || !(file->clear(),file->seekg(0)))
80 throw std::runtime_error("Failed to rewind "+name+" for the next decoder factory");
82 ++start;
85 return nullptr;
88 static SharedPtr<Decoder> GetDecoder(const String &name, UniquePtr<std::istream> file)
90 auto decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
91 if(!decoder) decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
92 if(!decoder) throw std::runtime_error("No decoder for "+name);
93 return decoder;
96 void RegisterDecoder(const String &name, UniquePtr<DecoderFactory> factory)
98 while(sDecoders.find(name) != sDecoders.end())
99 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
100 sDecoders.insert(std::make_pair(name, std::move(factory)));
103 UniquePtr<DecoderFactory> UnregisterDecoder(const String &name)
105 auto iter = sDecoders.find(name);
106 if(iter != sDecoders.end())
108 UniquePtr<DecoderFactory> factory = std::move(iter->second);
109 sDecoders.erase(iter);
110 return factory;
112 return nullptr;
116 class DefaultFileIOFactory : public FileIOFactory {
117 UniquePtr<std::istream> openFile(const String &name) override final
119 auto file = MakeUnique<std::ifstream>(name.c_str(), std::ios::binary);
120 if(!file->is_open()) file = nullptr;
121 return std::move(file);
124 static DefaultFileIOFactory sDefaultFileFactory;
126 static UniquePtr<FileIOFactory> sFileFactory;
127 UniquePtr<FileIOFactory> FileIOFactory::set(UniquePtr<FileIOFactory> factory)
129 std::swap(sFileFactory, factory);
130 return factory;
133 FileIOFactory &FileIOFactory::get()
135 FileIOFactory *factory = sFileFactory.get();
136 if(factory) return *factory;
137 return sDefaultFileFactory;
141 // Default message handler methods are no-ops.
142 MessageHandler::~MessageHandler()
146 void MessageHandler::deviceDisconnected(Device)
150 void MessageHandler::sourceStopped(Source*)
154 void MessageHandler::sourceForceStopped(Source*)
158 void MessageHandler::bufferLoading(const String&, ChannelConfig, SampleType, ALuint, const Vector<ALbyte>&)
162 String MessageHandler::resourceNotFound(const String&)
164 return String();
168 template<typename T>
169 static inline void LoadALFunc(T **func, const char *name)
170 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
172 static void LoadNothing(ALContext*) { }
174 static void LoadEFX(ALContext *ctx)
176 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
177 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
178 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
179 LoadALFunc(&ctx->alEffecti, "alEffecti");
180 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
181 LoadALFunc(&ctx->alEffectf, "alEffectf");
182 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
183 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
184 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
185 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
186 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
188 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
189 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
190 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
191 LoadALFunc(&ctx->alFilteri, "alFilteri");
192 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
193 LoadALFunc(&ctx->alFilterf, "alFilterf");
194 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
195 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
196 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
197 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
198 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
200 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
201 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
202 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
203 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
204 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
205 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
206 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
207 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
208 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
209 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
210 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
213 static void LoadSourceLatency(ALContext *ctx)
215 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
218 static const struct {
219 enum ALExtension extension;
220 const char name[32];
221 void (&loader)(ALContext*);
222 } ALExtensionList[] = {
223 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
225 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
226 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
227 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
229 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
230 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
231 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
233 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
234 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
236 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
238 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
239 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
243 ALContext *ALContext::sCurrentCtx = nullptr;
244 thread_local ALContext *ALContext::sThreadCurrentCtx = nullptr;
246 void ALContext::MakeCurrent(ALContext *context)
248 std::unique_lock<std::mutex> lock1, lock2;
249 if(sCurrentCtx)
250 lock1 = std::unique_lock<std::mutex>(sCurrentCtx->mContextMutex);
251 if(context && context != sCurrentCtx)
252 lock2 = std::unique_lock<std::mutex>(context->mContextMutex);
254 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
255 throw std::runtime_error("Call to alcMakeContextCurrent failed");
256 if(context)
258 context->addRef();
259 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
261 std::swap(sCurrentCtx, context);
262 if(context) context->decRef();
264 if(sThreadCurrentCtx)
265 sThreadCurrentCtx->decRef();
266 sThreadCurrentCtx = 0;
268 if(sCurrentCtx && sCurrentCtx != context)
270 lock2.unlock();
271 sCurrentCtx->mWakeThread.notify_all();
275 void ALContext::MakeThreadCurrent(ALContext *context)
277 if(!ALDeviceManager::SetThreadContext)
278 throw std::runtime_error("Thread-local contexts unsupported");
279 if(ALDeviceManager::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
280 throw std::runtime_error("Call to alcSetThreadContext failed");
281 if(context)
283 context->addRef();
284 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
286 if(sThreadCurrentCtx)
287 sThreadCurrentCtx->decRef();
288 sThreadCurrentCtx = context;
291 void ALContext::setupExts()
293 ALCdevice *device = mDevice->getDevice();
294 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
295 for(const auto &entry : ALExtensionList)
297 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
298 alcIsExtensionPresent(device, entry.name) :
299 alIsExtensionPresent(entry.name);
300 if(mHasExt[entry.extension]) entry.loader(this);
305 void ALContext::backgroundProc()
307 if(ALDeviceManager::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
308 ALDeviceManager::SetThreadContext(getContext());
310 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
311 std::chrono::milliseconds waketime(0);
312 std::unique_lock<std::mutex> ctxlock(mContextMutex);
313 while(!mQuitThread.load(std::memory_order_acquire))
316 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
317 auto source = mStreamingSources.begin();
318 while(source != mStreamingSources.end())
320 if(!(*source)->updateAsync())
321 source = mStreamingSources.erase(source);
322 else
323 ++source;
327 // Only do one pending buffer at a time. In case there's several large
328 // buffers to load, we still need to process streaming sources so they
329 // don't underrun.
330 RingBuffer::Data ringdata = mPendingBuffers.get_read_vector()[0];
331 if(ringdata.len > 0)
333 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata.buf);
334 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, pb->mName, this);
335 pb->~PendingBuffer();
336 mPendingBuffers.read_advance(1);
337 continue;
340 std::unique_lock<std::mutex> wakelock(mWakeMutex);
341 if(!mQuitThread.load(std::memory_order_acquire) && mPendingBuffers.read_space() == 0)
343 ctxlock.unlock();
345 ALuint interval = mWakeInterval.load();
346 if(!interval)
347 mWakeThread.wait(wakelock);
348 else
350 auto now = std::chrono::steady_clock::now() - basetime;
351 auto duration = std::chrono::milliseconds(interval);
352 while((waketime - now).count() <= 0) waketime += duration;
353 mWakeThread.wait_until(wakelock, waketime + basetime);
355 wakelock.unlock();
357 ctxlock.lock();
358 while(!mQuitThread.load(std::memory_order_acquire) &&
359 alcGetCurrentContext() != getContext())
360 mWakeThread.wait(ctxlock);
363 ctxlock.unlock();
365 if(ALDeviceManager::SetThreadContext)
366 ALDeviceManager::SetThreadContext(nullptr);
370 ALContext::ALContext(ALCcontext *context, ALDevice *device)
371 : mListener(this), mContext(context), mDevice(device), mRefs(0),
372 mHasExt{false}, mPendingBuffers(16, sizeof(PendingBuffer)),
373 mWakeInterval(0), mQuitThread(false), mIsConnected(true), mIsBatching(false),
374 alGetSourcei64vSOFT(0),
375 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
376 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
377 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
378 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
379 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
380 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
381 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
382 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
383 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
387 ALContext::~ALContext()
389 auto ringdata = mPendingBuffers.get_read_vector();
390 if(ringdata[0].len > 0)
392 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(ringdata[0].buf);
393 for(size_t i = 0;i < ringdata[0].len;i++)
394 pb[i].~PendingBuffer();
395 pb = reinterpret_cast<PendingBuffer*>(ringdata[1].buf);
396 for(size_t i = 0;i < ringdata[1].len;i++)
397 pb[i].~PendingBuffer();
399 mPendingBuffers.read_advance(ringdata[0].len + ringdata[1].len);
404 Device ALContext::getDevice()
406 return Device(mDevice);
409 void ALContext::destroy()
411 if(mRefs.load() != 0)
412 throw std::runtime_error("Context is in use");
413 if(!mBuffers.empty())
414 throw std::runtime_error("Trying to destroy a context with buffers");
416 if(mThread.joinable())
418 std::unique_lock<std::mutex> lock(mWakeMutex);
419 mQuitThread.store(true, std::memory_order_release);
420 lock.unlock();
421 mWakeThread.notify_all();
422 mThread.join();
425 alcDestroyContext(mContext);
426 mContext = nullptr;
428 mDevice->removeContext(this);
432 void ALContext::startBatch()
434 alcSuspendContext(mContext);
435 mIsBatching = true;
438 void ALContext::endBatch()
440 alcProcessContext(mContext);
441 mIsBatching = false;
445 Listener *ALContext::getListener()
447 return &mListener;
451 SharedPtr<MessageHandler> ALContext::setMessageHandler(SharedPtr<MessageHandler> handler)
453 std::lock_guard<std::mutex> lock(mContextMutex);
454 mMessage.swap(handler);
455 return handler;
459 void ALContext::setAsyncWakeInterval(ALuint msec)
461 mWakeInterval.store(msec);
462 mWakeMutex.lock(); mWakeMutex.unlock();
463 mWakeThread.notify_all();
466 ALuint ALContext::getAsyncWakeInterval() const
468 return mWakeInterval.load();
472 SharedPtr<Decoder> ALContext::createDecoder(const String &name)
474 auto file = FileIOFactory::get().openFile(name);
475 if(file) return GetDecoder(name, std::move(file));
477 // Resource not found. Try to find a substitute.
478 if(!mMessage.get()) throw std::runtime_error("Failed to open "+name);
479 String oldname = name;
480 do {
481 String newname(mMessage->resourceNotFound(oldname));
482 if(newname.empty())
483 throw std::runtime_error("Failed to open "+oldname);
484 file = FileIOFactory::get().openFile(newname);
485 oldname = std::move(newname);
486 } while(!file);
488 return GetDecoder(oldname, std::move(file));
492 bool ALContext::isSupported(ChannelConfig channels, SampleType type) const
494 return GetFormat(channels, type) != AL_NONE;
498 Buffer ALContext::getBuffer(const String &name)
500 CheckContext(this);
502 auto hasher = std::hash<String>();
503 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
504 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
505 { return hasher(lhs->getName()) < rhs; }
507 if(iter != mBuffers.end() && (*iter)->getName() == name)
509 // Ensure the buffer is loaded before returning. getBuffer guarantees
510 // the returned buffer is loaded.
511 ALBuffer *buffer = iter->get();
512 while(buffer->getLoadStatus() == BufferLoadStatus::Pending)
513 std::this_thread::yield();
514 return Buffer(buffer);
516 // NOTE: 'iter' is used later to insert a new entry!
518 auto decoder = createDecoder(name);
520 ALuint srate = decoder->getFrequency();
521 ChannelConfig chans = decoder->getChannelConfig();
522 SampleType type = decoder->getSampleType();
523 ALuint frames = decoder->getLength();
525 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
526 frames = decoder->read(&data[0], frames);
527 if(!frames) throw std::runtime_error("No samples for buffer");
528 data.resize(FramesToBytes(frames, chans, type));
530 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
531 if(loop_pts.first >= loop_pts.second)
532 loop_pts = std::make_pair(0, frames);
533 else
535 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
536 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
539 // Get the format before calling the bufferLoading message handler, to
540 // ensure it's something OpenAL can handle.
541 ALenum format = GetFormat(chans, type);
542 if(format == AL_NONE)
544 std::stringstream sstr;
545 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
546 throw std::runtime_error(sstr.str());
549 if(mMessage.get())
550 mMessage->bufferLoading(name, chans, type, srate, data);
552 alGetError();
553 ALuint bid = 0;
554 try {
555 alGenBuffers(1, &bid);
556 alBufferData(bid, format, &data[0], data.size(), srate);
557 if(hasExtension(SOFT_loop_points))
559 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
560 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
562 if(alGetError() != AL_NO_ERROR)
563 throw std::runtime_error("Failed to buffer data");
565 return Buffer(mBuffers.insert(iter,
566 MakeUnique<ALBuffer>(this, bid, srate, chans, type, true, name)
567 )->get());
569 catch(...) {
570 alDeleteBuffers(1, &bid);
571 throw;
575 Buffer ALContext::getBufferAsync(const String &name)
577 CheckContext(this);
579 auto hasher = std::hash<String>();
580 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
581 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
582 { return hasher(lhs->getName()) < rhs; }
584 if(iter != mBuffers.end() && (*iter)->getName() == name)
585 return Buffer(iter->get());
586 // NOTE: 'iter' is used later to insert a new entry!
588 auto decoder = createDecoder(name);
590 ALuint srate = decoder->getFrequency();
591 ChannelConfig chans = decoder->getChannelConfig();
592 SampleType type = decoder->getSampleType();
593 ALuint frames = decoder->getLength();
594 if(!frames) throw std::runtime_error("No samples for buffer");
596 ALenum format = GetFormat(chans, type);
597 if(format == AL_NONE)
599 std::stringstream sstr;
600 sstr<< "Format not supported ("<<GetSampleTypeName(type)<<", "<<GetChannelConfigName(chans)<<")";
601 throw std::runtime_error(sstr.str());
604 alGetError();
605 ALuint bid = 0;
606 alGenBuffers(1, &bid);
607 if(alGetError() != AL_NO_ERROR)
608 throw std::runtime_error("Failed to buffer data");
610 auto buffer = MakeUnique<ALBuffer>(this, bid, srate, chans, type, false, name);
612 if(mThread.get_id() == std::thread::id())
613 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
615 while(mPendingBuffers.write_space() == 0)
616 std::this_thread::yield();
618 RingBuffer::Data ringdata = mPendingBuffers.get_write_vector()[0];
619 new(ringdata.buf) PendingBuffer{name, buffer.get(), decoder, format, frames};
620 mPendingBuffers.write_advance(1);
621 mWakeMutex.lock(); mWakeMutex.unlock();
622 mWakeThread.notify_all();
624 return Buffer(mBuffers.insert(iter, std::move(buffer))->get());
628 void ALContext::removeBuffer(const String &name)
630 CheckContext(this);
631 auto hasher = std::hash<String>();
632 auto iter = std::lower_bound(mBuffers.begin(), mBuffers.end(), hasher(name),
633 [hasher](const UniquePtr<ALBuffer> &lhs, size_t rhs) -> bool
634 { return hasher(lhs->getName()) < rhs; }
636 if(iter != mBuffers.end() && (*iter)->getName() == name)
638 (*iter)->cleanup();
639 mBuffers.erase(iter);
643 void ALContext::removeBuffer(Buffer buffer)
645 removeBuffer(buffer.getName());
649 ALuint ALContext::getSourceId(ALuint maxprio)
651 CheckContext(this);
653 ALuint id = 0;
654 if(mSourceIds.empty())
656 alGetError();
657 alGenSources(1, &id);
658 if(alGetError() == AL_NO_ERROR)
659 return id;
661 ALSource *lowest = nullptr;
662 for(ALSource *src : mUsedSources)
664 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
665 lowest = src;
667 if(lowest && lowest->getPriority() < maxprio)
669 lowest->makeStopped();
670 if(mMessage.get())
671 mMessage->sourceForceStopped(lowest);
674 if(mSourceIds.empty())
675 throw std::runtime_error("No available sources");
677 id = mSourceIds.top();
678 mSourceIds.pop();
679 return id;
683 Source *ALContext::createSource()
685 CheckContext(this);
687 ALSource *source;
688 if(!mFreeSources.empty())
690 source = mFreeSources.front();
691 mFreeSources.pop();
693 else
695 mAllSources.emplace_back(this);
696 source = &mAllSources.back();
698 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
699 if(iter == mUsedSources.end() || *iter != source)
700 mUsedSources.insert(iter, source);
701 return source;
704 void ALContext::freeSource(ALSource *source)
706 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
707 if(iter != mUsedSources.end() && *iter == source) mUsedSources.erase(iter);
708 mFreeSources.push(source);
712 void ALContext::addStream(ALSource *source)
714 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
715 if(mThread.get_id() == std::thread::id())
716 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
717 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
718 if(iter == mStreamingSources.end() || *iter != source)
719 mStreamingSources.insert(iter, source);
722 void ALContext::removeStream(ALSource *source)
724 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
725 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
726 if(iter != mStreamingSources.end() && *iter == source)
727 mStreamingSources.erase(iter);
730 void ALContext::removeStreamNoLock(ALSource *source)
732 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
733 if(iter != mStreamingSources.end() && *iter == source)
734 mStreamingSources.erase(iter);
738 AuxiliaryEffectSlot *ALContext::createAuxiliaryEffectSlot()
740 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
741 throw std::runtime_error("AuxiliaryEffectSlots not supported");
742 CheckContext(this);
744 alGetError();
745 ALuint id = 0;
746 alGenAuxiliaryEffectSlots(1, &id);
747 if(alGetError() != AL_NO_ERROR)
748 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
749 try {
750 return new ALAuxiliaryEffectSlot(this, id);
752 catch(...) {
753 alDeleteAuxiliaryEffectSlots(1, &id);
754 throw;
759 Effect *ALContext::createEffect()
761 if(!hasExtension(EXT_EFX))
762 throw std::runtime_error("Effects not supported");
763 CheckContext(this);
765 alGetError();
766 ALuint id = 0;
767 alGenEffects(1, &id);
768 if(alGetError() != AL_NO_ERROR)
769 throw std::runtime_error("Failed to create Effect");
770 try {
771 return new ALEffect(this, id);
773 catch(...) {
774 alDeleteEffects(1, &id);
775 throw;
780 SourceGroup *ALContext::createSourceGroup(String name)
782 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
783 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
784 { return lhs->getName() < rhs; }
786 if(iter != mSourceGroups.end() && (*iter)->getName() == name)
787 throw std::runtime_error("Duplicate source group name");
788 iter = mSourceGroups.insert(iter, MakeUnique<ALSourceGroup>(this, std::move(name)));
789 return iter->get();
792 SourceGroup *ALContext::getSourceGroup(const String &name)
794 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), name,
795 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
796 { return lhs->getName() < rhs; }
798 if(iter == mSourceGroups.end() || (*iter)->getName() != name)
799 throw std::runtime_error("Source group not found");
800 return iter->get();
803 void ALContext::freeSourceGroup(ALSourceGroup *group)
805 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group->getName(),
806 [](const UniquePtr<ALSourceGroup> &lhs, const String &rhs) -> bool
807 { return lhs->getName() < rhs; }
809 if(iter != mSourceGroups.end() && iter->get() == group)
810 mSourceGroups.erase(iter);
814 void ALContext::setDopplerFactor(ALfloat factor)
816 if(!(factor >= 0.0f))
817 throw std::runtime_error("Doppler factor out of range");
818 CheckContext(this);
819 alDopplerFactor(factor);
823 void ALContext::setSpeedOfSound(ALfloat speed)
825 if(!(speed > 0.0f))
826 throw std::runtime_error("Speed of sound out of range");
827 CheckContext(this);
828 alSpeedOfSound(speed);
832 void ALContext::setDistanceModel(DistanceModel model)
834 CheckContext(this);
835 alDistanceModel((ALenum)model);
839 void ALContext::update()
841 CheckContext(this);
842 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fn(&ALSource::updateNoCtxCheck));
843 if(!mWakeInterval.load())
845 // For performance reasons, don't wait for the thread's mutex. This
846 // should be called often enough to keep up with any and all streams
847 // regardless.
848 mWakeThread.notify_all();
851 if(hasExtension(EXT_disconnect) && mIsConnected)
853 ALCint connected;
854 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
855 if(!connected && mMessage.get()) mMessage->deviceDisconnected(Device(mDevice));
856 mIsConnected = connected;
860 void Context::destroy()
862 pImpl->destroy();
863 pImpl = nullptr;
865 DECL_THUNK0(Device, Context, getDevice,)
866 DECL_THUNK0(void, Context, startBatch,)
867 DECL_THUNK0(void, Context, endBatch,)
868 DECL_THUNK0(Listener*, Context, getListener,)
869 DECL_THUNK1(SharedPtr<MessageHandler>, Context, setMessageHandler,, SharedPtr<MessageHandler>)
870 DECL_THUNK0(SharedPtr<MessageHandler>, Context, getMessageHandler, const)
871 DECL_THUNK1(void, Context, setAsyncWakeInterval,, ALuint)
872 DECL_THUNK0(ALuint, Context, getAsyncWakeInterval, const)
873 DECL_THUNK1(SharedPtr<Decoder>, Context, createDecoder,, const String&)
874 DECL_THUNK2(bool, Context, isSupported, const, ChannelConfig, SampleType)
875 DECL_THUNK1(Buffer, Context, getBuffer,, const String&)
876 DECL_THUNK1(Buffer, Context, getBufferAsync,, const String&)
877 DECL_THUNK1(void, Context, removeBuffer,, const String&)
878 DECL_THUNK1(void, Context, removeBuffer,, Buffer)
879 DECL_THUNK0(Source*, Context, createSource,)
880 DECL_THUNK0(AuxiliaryEffectSlot*, Context, createAuxiliaryEffectSlot,)
881 DECL_THUNK0(Effect*, Context, createEffect,)
882 DECL_THUNK1(SourceGroup*, Context, createSourceGroup,, String)
883 DECL_THUNK1(SourceGroup*, Context, getSourceGroup,, const String&)
884 DECL_THUNK1(void, Context, setDopplerFactor,, ALfloat)
885 DECL_THUNK1(void, Context, setSpeedOfSound,, ALfloat)
886 DECL_THUNK1(void, Context, setDistanceModel,, DistanceModel)
887 DECL_THUNK0(void, Context, update,)
890 void Context::MakeCurrent(Context context)
891 { ALContext::MakeCurrent(context.pImpl); }
893 void Context::MakeCurrent(std::nullptr_t)
894 { ALContext::MakeCurrent(nullptr); }
896 Context Context::GetCurrent()
897 { return Context(ALContext::GetCurrent()); }
899 void Context::MakeThreadCurrent(Context context)
900 { ALContext::MakeThreadCurrent(context.pImpl); }
902 void Context::MakeThreadCurrent(std::nullptr_t)
903 { ALContext::MakeThreadCurrent(nullptr); }
905 Context Context::GetThreadCurrent()
906 { return Context(ALContext::GetThreadCurrent()); }
909 void ALListener::setGain(ALfloat gain)
911 if(!(gain >= 0.0f))
912 throw std::runtime_error("Gain out of range");
913 CheckContext(mContext);
914 alListenerf(AL_GAIN, gain);
918 void ALListener::setPosition(ALfloat x, ALfloat y, ALfloat z)
920 CheckContext(mContext);
921 alListener3f(AL_POSITION, x, y, z);
924 void ALListener::setPosition(const ALfloat *pos)
926 CheckContext(mContext);
927 alListenerfv(AL_POSITION, pos);
930 void ALListener::setVelocity(ALfloat x, ALfloat y, ALfloat z)
932 CheckContext(mContext);
933 alListener3f(AL_VELOCITY, x, y, z);
936 void ALListener::setVelocity(const ALfloat *vel)
938 CheckContext(mContext);
939 alListenerfv(AL_VELOCITY, vel);
942 void ALListener::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
944 CheckContext(mContext);
945 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
946 alListenerfv(AL_ORIENTATION, ori);
949 void ALListener::setOrientation(const ALfloat *at, const ALfloat *up)
951 CheckContext(mContext);
952 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
953 alListenerfv(AL_ORIENTATION, ori);
956 void ALListener::setOrientation(const ALfloat *ori)
958 CheckContext(mContext);
959 alListenerfv(AL_ORIENTATION, ori);
962 void ALListener::setMetersPerUnit(ALfloat m_u)
964 if(!(m_u > 0.0f))
965 throw std::runtime_error("Invalid meters per unit");
966 CheckContext(mContext);
967 if(mContext->hasExtension(EXT_EFX))
968 alListenerf(AL_METERS_PER_UNIT, m_u);