Add a method to retrieve a buffer's name
[alure.git] / src / context.cpp
blobf78e617c15a7433a66f9005429626fb619b90c90
2 #include "config.h"
4 #include "context.h"
6 #include <stdexcept>
7 #include <algorithm>
8 #include <memory>
9 #include <iostream>
10 #include <sstream>
11 #include <fstream>
12 #include <cstring>
13 #include <map>
14 #include <new>
16 #include "alc.h"
18 #ifdef HAVE_VORBISFILE
19 #include "decoders/vorbisfile.hpp"
20 #endif
21 #ifdef HAVE_LIBFLAC
22 #include "decoders/flac.hpp"
23 #endif
24 #ifdef HAVE_OPUSFILE
25 #include "decoders/opusfile.hpp"
26 #endif
27 #ifdef HAVE_LIBSNDFILE
28 #include "decoders/sndfile.hpp"
29 #endif
30 #ifdef HAVE_MPG123
31 #include "decoders/mpg123.hpp"
32 #endif
33 #include "decoders/wave.hpp"
35 #include "devicemanager.h"
36 #include "device.h"
37 #include "buffer.h"
38 #include "source.h"
39 #include "auxeffectslot.h"
40 #include "effect.h"
41 #include <sourcegroup.h>
43 namespace alure
46 static const std::pair<String,UniquePtr<DecoderFactory>> sDefaultDecoders[] = {
47 { "_alure_int_wave", MakeUnique<WaveDecoderFactory>() },
49 #ifdef HAVE_VORBISFILE
50 { "_alure_int_vorbis", MakeUnique<VorbisFileDecoderFactory>() },
51 #endif
52 #ifdef HAVE_LIBFLAC
53 { "_alure_int_flac", MakeUnique<FlacDecoderFactory>() },
54 #endif
55 #ifdef HAVE_OPUSFILE
56 { "_alure_int_opus", MakeUnique<OpusFileDecoderFactory>() },
57 #endif
58 #ifdef HAVE_LIBSNDFILE
59 { "_alure_int_sndfile", MakeUnique<SndFileDecoderFactory>() },
60 #endif
61 #ifdef HAVE_MPG123
62 { "_alure_int_mpg123", MakeUnique<Mpg123DecoderFactory>() },
63 #endif
66 static std::map<String,UniquePtr<DecoderFactory>> sDecoders;
69 template<typename T>
70 static SharedPtr<Decoder> GetDecoder(const String &name, SharedPtr<std::istream> file, T start, T end)
72 while(start != end)
74 DecoderFactory *factory = start->second.get();
75 auto decoder = factory->createDecoder(file);
76 if(decoder) return decoder;
78 file->clear();
79 if(!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, SharedPtr<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 std::move(factory);
112 return nullptr;
116 class DefaultFileIOFactory : public FileIOFactory {
117 virtual SharedPtr<std::istream> openFile(const String &name)
119 auto file = MakeShared<std::ifstream>(name.c_str(), std::ios::binary);
120 if(!file->is_open()) file.reset();
121 return 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*, bool)
154 void MessageHandler::bufferLoading(const String&, ChannelConfig, SampleType, ALuint, const Vector<ALbyte>&)
158 String MessageHandler::resourceNotFound(const String&)
160 return String();
164 template<typename T>
165 static inline void LoadALFunc(T **func, const char *name)
166 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
168 static void LoadNothing(ALContext*) { }
170 static void LoadEFX(ALContext *ctx)
172 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
173 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
174 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
175 LoadALFunc(&ctx->alEffecti, "alEffecti");
176 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
177 LoadALFunc(&ctx->alEffectf, "alEffectf");
178 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
179 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
180 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
181 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
182 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
184 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
185 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
186 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
187 LoadALFunc(&ctx->alFilteri, "alFilteri");
188 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
189 LoadALFunc(&ctx->alFilterf, "alFilterf");
190 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
191 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
192 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
193 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
194 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
196 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
197 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
198 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
199 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
200 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
201 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
202 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
203 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
204 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
205 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
206 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
209 static void LoadSourceLatency(ALContext *ctx)
211 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
214 static const struct {
215 enum ALExtension extension;
216 const char name[32];
217 void (&loader)(ALContext*);
218 } ALExtensionList[] = {
219 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
221 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
222 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
223 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
225 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
226 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
227 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
229 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
230 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
232 { EXT_disconnect, "ALC_EXT_disconnect", LoadNothing },
234 { EXT_SOURCE_RADIUS, "AL_EXT_SOURCE_RADIUS", LoadNothing },
235 { EXT_STEREO_ANGLES, "AL_EXT_STEREO_ANGLES", LoadNothing },
239 ALContext *ALContext::sCurrentCtx = nullptr;
240 thread_local ALContext *ALContext::sThreadCurrentCtx = nullptr;
242 void ALContext::MakeCurrent(ALContext *context)
244 std::unique_lock<std::mutex> lock1, lock2;
245 if(sCurrentCtx)
246 lock1 = std::unique_lock<std::mutex>(sCurrentCtx->mContextMutex);
247 if(context && context != sCurrentCtx)
248 lock2 = std::unique_lock<std::mutex>(context->mContextMutex);
250 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
251 throw std::runtime_error("Call to alcMakeContextCurrent failed");
252 if(context)
254 context->addRef();
255 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
257 std::swap(sCurrentCtx, context);
258 if(context) context->decRef();
260 if(sThreadCurrentCtx)
261 sThreadCurrentCtx->decRef();
262 sThreadCurrentCtx = 0;
264 if(sCurrentCtx && sCurrentCtx != context)
266 lock2.unlock();
267 sCurrentCtx->mWakeThread.notify_all();
271 void ALContext::MakeThreadCurrent(ALContext *context)
273 if(!ALDeviceManager::SetThreadContext)
274 throw std::runtime_error("Thread-local contexts unsupported");
275 if(ALDeviceManager::SetThreadContext(context ? context->getContext() : nullptr) == ALC_FALSE)
276 throw std::runtime_error("Call to alcSetThreadContext failed");
277 if(context)
279 context->addRef();
280 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
282 if(sThreadCurrentCtx)
283 sThreadCurrentCtx->decRef();
284 sThreadCurrentCtx = context;
287 void ALContext::setupExts()
289 ALCdevice *device = mDevice->getDevice();
290 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
291 for(const auto &entry : ALExtensionList)
293 mHasExt[entry.extension] = (strncmp(entry.name, "ALC", 3) == 0) ?
294 alcIsExtensionPresent(device, entry.name) :
295 alIsExtensionPresent(entry.name);
296 if(mHasExt[entry.extension]) entry.loader(this);
301 void ALContext::backgroundProc()
303 if(ALDeviceManager::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
304 ALDeviceManager::SetThreadContext(getContext());
306 std::chrono::steady_clock::time_point basetime = std::chrono::steady_clock::now();
307 std::chrono::milliseconds waketime(0);
308 std::unique_lock<std::mutex> ctxlock(mContextMutex);
309 while(!mQuitThread)
312 std::lock_guard<std::mutex> srclock(mSourceStreamMutex);
313 auto source = mStreamingSources.begin();
314 while(source != mStreamingSources.end())
316 if(!(*source)->updateAsync())
317 source = mStreamingSources.erase(source);
318 else
319 ++source;
323 // Only do one pending buffer at a time. In case there's several large
324 // buffers to load, we still need to process streaming sources so they
325 // don't underrun.
326 ll_ringbuffer_data_t vec[2];
327 ll_ringbuffer_get_read_vector(mPendingBuffers.get(), vec);
328 if(vec[0].len > 0)
330 PendingBuffer *pb = reinterpret_cast<PendingBuffer*>(vec[0].buf);
331 pb->mBuffer->load(pb->mFrames, pb->mFormat, pb->mDecoder, pb->mName, this);
332 pb->~PendingBuffer();
333 ll_ringbuffer_read_advance(mPendingBuffers.get(), 1);
334 continue;
337 std::unique_lock<std::mutex> wakelock(mWakeMutex);
338 if(!mQuitThread && ll_ringbuffer_read_space(mPendingBuffers.get()) == 0)
340 ctxlock.unlock();
342 ALuint interval = mWakeInterval.load();
343 if(!interval)
344 mWakeThread.wait(wakelock);
345 else
347 auto now = std::chrono::steady_clock::now() - basetime;
348 auto duration = std::chrono::milliseconds(interval);
349 while((waketime - now).count() <= 0) waketime += duration;
350 mWakeThread.wait_until(wakelock, waketime + basetime);
352 wakelock.unlock();
354 ctxlock.lock();
355 while(!mQuitThread && alcGetCurrentContext() != getContext())
356 mWakeThread.wait(ctxlock);
359 ctxlock.unlock();
361 if(ALDeviceManager::SetThreadContext)
362 ALDeviceManager::SetThreadContext(nullptr);
366 ALContext::ALContext(ALCcontext *context, ALDevice *device)
367 : mContext(context), mDevice(device), mRefs(0),
368 mHasExt{false}, mPendingBuffers(ll_ringbuffer_create(16, sizeof(PendingBuffer)), ll_ringbuffer_free),
369 mWakeInterval(0), mQuitThread(false), mIsConnected(true), mIsBatching(false),
370 alGetSourcei64vSOFT(0),
371 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
372 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
373 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
374 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
375 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
376 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
377 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
378 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
379 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
383 ALContext::~ALContext()
385 mDevice->removeContext(this);
389 Device *ALContext::getDevice()
391 return mDevice;
394 void ALContext::destroy()
396 if(mRefs.load() != 0)
397 throw std::runtime_error("Context is in use");
398 if(!mBuffers.empty())
399 throw std::runtime_error("Trying to destroy a context with buffers");
401 if(mThread.joinable())
403 std::unique_lock<std::mutex> lock(mWakeMutex);
404 mQuitThread = true;
405 lock.unlock();
406 mWakeThread.notify_all();
407 mThread.join();
410 alcDestroyContext(mContext);
411 mContext = nullptr;
412 delete this;
416 void ALContext::startBatch()
418 alcSuspendContext(mContext);
419 mIsBatching = true;
422 void ALContext::endBatch()
424 alcProcessContext(mContext);
425 mIsBatching = false;
429 Listener *ALContext::getListener()
431 return this;
435 SharedPtr<MessageHandler> ALContext::setMessageHandler(SharedPtr<MessageHandler> handler)
437 std::lock_guard<std::mutex> lock(mContextMutex);
438 mMessage.swap(handler);
439 return handler;
443 void ALContext::setAsyncWakeInterval(ALuint msec)
445 mWakeInterval.store(msec);
446 mWakeMutex.lock(); mWakeMutex.unlock();
447 mWakeThread.notify_all();
450 ALuint ALContext::getAsyncWakeInterval() const
452 return mWakeInterval.load();
456 SharedPtr<Decoder> ALContext::createDecoder(const String &name)
458 auto file = FileIOFactory::get().openFile(name);
459 if(file.get()) return GetDecoder(name, file);
461 // Resource not found. Try to find a substitute.
462 if(!mMessage.get()) throw std::runtime_error("Failed to open "+name);
463 String oldname = name;
464 do {
465 String newname(mMessage->resourceNotFound(oldname));
466 if(newname.empty())
467 throw std::runtime_error("Failed to open "+oldname);
468 file = FileIOFactory::get().openFile(newname);
469 oldname = std::move(newname);
470 } while(!file.get());
472 return GetDecoder(oldname, file);
476 Buffer *ALContext::getBuffer(const String &name)
478 CheckContext(this);
480 auto iter = mBuffers.find(name);
481 if(iter != mBuffers.end())
483 // Ensure the buffer is loaded before returning. getBuffer guarantees
484 // the returned buffer is loaded.
485 ALBuffer *buffer = iter->second;
486 while(buffer->getLoadStatus() == BufferLoadStatus::Pending)
487 std::this_thread::yield();
488 return buffer;
491 auto decoder = createDecoder(name);
493 ALuint srate = decoder->getFrequency();
494 ChannelConfig chans = decoder->getChannelConfig();
495 SampleType type = decoder->getSampleType();
496 ALuint frames = decoder->getLength();
498 Vector<ALbyte> data(FramesToBytes(frames, chans, type));
499 frames = decoder->read(&data[0], frames);
500 if(!frames) throw std::runtime_error("No samples for buffer");
501 data.resize(FramesToBytes(frames, chans, type));
503 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
504 if(loop_pts.first >= loop_pts.second)
505 loop_pts = std::make_pair(0, frames);
506 else
508 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
509 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
512 // Get the format before calling the bufferLoading message handler, to
513 // ensure it's something OpenAL can handle.
514 ALenum format = GetFormat(chans, type);
516 if(mMessage.get())
517 mMessage->bufferLoading(name, chans, type, srate, data);
519 alGetError();
520 ALuint bid = 0;
521 try {
522 alGenBuffers(1, &bid);
523 alBufferData(bid, format, &data[0], data.size(), srate);
524 if(hasExtension(SOFT_loop_points))
526 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
527 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
529 if(alGetError() != AL_NO_ERROR)
530 throw std::runtime_error("Failed to buffer data");
532 ALBuffer *buffer = new ALBuffer(this, bid, srate, chans, type, true, name);
533 return mBuffers.insert(std::make_pair(name, buffer)).first->second;
535 catch(...) {
536 alDeleteBuffers(1, &bid);
537 throw;
541 Buffer *ALContext::getBufferAsync(const String &name)
543 CheckContext(this);
545 auto iter = mBuffers.find(name);
546 if(iter != mBuffers.end()) return iter->second;
548 auto decoder = createDecoder(name);
550 ALuint srate = decoder->getFrequency();
551 ChannelConfig chans = decoder->getChannelConfig();
552 SampleType type = decoder->getSampleType();
553 ALuint frames = decoder->getLength();
554 if(!frames) throw std::runtime_error("No samples for buffer");
556 ALenum format = GetFormat(chans, type);
558 alGetError();
559 ALuint bid = 0;
560 alGenBuffers(1, &bid);
561 if(alGetError() != AL_NO_ERROR)
562 throw std::runtime_error("Failed to buffer data");
564 ALBuffer *buffer = new ALBuffer(this, bid, srate, chans, type, false, name);
566 if(mThread.get_id() == std::thread::id())
567 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
569 while(ll_ringbuffer_write_space(mPendingBuffers.get()) == 0)
570 std::this_thread::yield();
572 ll_ringbuffer_data_t vec[2];
573 ll_ringbuffer_get_write_vector(mPendingBuffers.get(), vec);
574 new(vec[0].buf) PendingBuffer{name, buffer, decoder, format, frames};
575 ll_ringbuffer_write_advance(mPendingBuffers.get(), 1);
576 mWakeMutex.lock(); mWakeMutex.unlock();
577 mWakeThread.notify_all();
579 return mBuffers.insert(std::make_pair(name, buffer)).first->second;
583 void ALContext::removeBuffer(const String &name)
585 CheckContext(this);
586 auto iter = mBuffers.find(name);
587 if(iter != mBuffers.end())
589 ALBuffer *albuf = iter->second;
590 albuf->cleanup();
591 mBuffers.erase(iter);
595 void ALContext::removeBuffer(Buffer *buffer)
597 CheckContext(this);
598 auto iter = mBuffers.begin();
599 while(iter != mBuffers.end())
601 ALBuffer *albuf = iter->second;
602 if(albuf == buffer)
604 albuf->cleanup();
605 mBuffers.erase(iter);
606 break;
608 ++iter;
613 ALuint ALContext::getSourceId(ALuint maxprio)
615 CheckContext(this);
617 ALuint id = 0;
618 if(mSourceIds.empty())
620 alGetError();
621 alGenSources(1, &id);
622 if(alGetError() == AL_NO_ERROR)
623 return id;
625 ALSource *lowest = nullptr;
626 for(ALSource *src : mUsedSources)
628 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
629 lowest = src;
631 if(lowest && lowest->getPriority() < maxprio)
633 lowest->stop();
634 if(mMessage.get())
635 mMessage->sourceStopped(lowest, true);
638 if(mSourceIds.empty())
639 throw std::runtime_error("No available sources");
641 id = mSourceIds.top();
642 mSourceIds.pop();
643 return id;
647 Source *ALContext::getSource()
649 CheckContext(this);
651 ALSource *source;
652 if(mFreeSources.empty())
654 mAllSources.emplace_back(this);
655 source = &mAllSources.back();
657 else
659 source = mFreeSources.back();
660 mFreeSources.pop();
662 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
663 if(iter == mUsedSources.end() || *iter != source)
664 mUsedSources.insert(iter, source);
665 return source;
668 void ALContext::freeSource(ALSource *source)
670 auto iter = std::lower_bound(mUsedSources.begin(), mUsedSources.end(), source);
671 if(iter != mUsedSources.end() && *iter == source) mUsedSources.erase(iter);
672 mFreeSources.push(source);
676 void ALContext::addStream(ALSource *source)
678 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
679 if(mThread.get_id() == std::thread::id())
680 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
681 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
682 if(iter == mStreamingSources.end() || *iter != source)
683 mStreamingSources.insert(iter, source);
686 void ALContext::removeStream(ALSource *source)
688 std::lock_guard<std::mutex> lock(mSourceStreamMutex);
689 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
690 if(iter != mStreamingSources.end() && *iter == source)
691 mStreamingSources.erase(iter);
694 void ALContext::removeStreamNoLock(ALSource *source)
696 auto iter = std::lower_bound(mStreamingSources.begin(), mStreamingSources.end(), source);
697 if(iter != mStreamingSources.end() && *iter == source)
698 mStreamingSources.erase(iter);
702 AuxiliaryEffectSlot *ALContext::createAuxiliaryEffectSlot()
704 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
705 throw std::runtime_error("AuxiliaryEffectSlots not supported");
706 CheckContext(this);
708 alGetError();
709 ALuint id = 0;
710 alGenAuxiliaryEffectSlots(1, &id);
711 if(alGetError() != AL_NO_ERROR)
712 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
713 try {
714 return new ALAuxiliaryEffectSlot(this, id);
716 catch(...) {
717 alDeleteAuxiliaryEffectSlots(1, &id);
718 throw;
723 Effect *ALContext::createEffect()
725 if(!hasExtension(EXT_EFX))
726 throw std::runtime_error("Effects not supported");
727 CheckContext(this);
729 alGetError();
730 ALuint id = 0;
731 alGenEffects(1, &id);
732 if(alGetError() != AL_NO_ERROR)
733 throw std::runtime_error("Failed to create Effect");
734 try {
735 return new ALEffect(this, id);
737 catch(...) {
738 alDeleteEffects(1, &id);
739 throw;
744 SourceGroup *ALContext::createSourceGroup()
746 ALSourceGroup *group = new ALSourceGroup(this);
747 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group);
748 mSourceGroups.insert(iter, group);
749 return group;
752 void ALContext::freeSourceGroup(ALSourceGroup *group)
754 auto iter = std::lower_bound(mSourceGroups.begin(), mSourceGroups.end(), group);
755 if(iter != mSourceGroups.end() && *iter != group)
756 mSourceGroups.erase(iter);
760 void ALContext::setDopplerFactor(ALfloat factor)
762 if(!(factor >= 0.0f))
763 throw std::runtime_error("Doppler factor out of range");
764 CheckContext(this);
765 alDopplerFactor(factor);
769 void ALContext::setSpeedOfSound(ALfloat speed)
771 if(!(speed > 0.0f))
772 throw std::runtime_error("Speed of sound out of range");
773 CheckContext(this);
774 alSpeedOfSound(speed);
778 void ALContext::setDistanceModel(DistanceModel model)
780 CheckContext(this);
781 alDistanceModel((ALenum)model);
785 void ALContext::update()
787 CheckContext(this);
788 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fn(&ALSource::updateNoCtxCheck));
789 if(!mWakeInterval.load())
791 // For performance reasons, don't wait for the thread's mutex. This
792 // should be called often enough to keep up with any and all streams
793 // regardless.
794 mWakeThread.notify_all();
797 if(hasExtension(EXT_disconnect) && mIsConnected)
799 ALCint connected;
800 alcGetIntegerv(alcGetContextsDevice(mContext), ALC_CONNECTED, 1, &connected);
801 if(!connected && mMessage.get()) mMessage->deviceDisconnected(mDevice);
802 mIsConnected = connected;
807 void ALContext::setGain(ALfloat gain)
809 if(!(gain >= 0.0f))
810 throw std::runtime_error("Gain out of range");
811 CheckContext(this);
812 alListenerf(AL_GAIN, gain);
816 void ALContext::setPosition(ALfloat x, ALfloat y, ALfloat z)
818 CheckContext(this);
819 alListener3f(AL_POSITION, x, y, z);
822 void ALContext::setPosition(const ALfloat *pos)
824 CheckContext(this);
825 alListenerfv(AL_POSITION, pos);
828 void ALContext::setVelocity(ALfloat x, ALfloat y, ALfloat z)
830 CheckContext(this);
831 alListener3f(AL_VELOCITY, x, y, z);
834 void ALContext::setVelocity(const ALfloat *vel)
836 CheckContext(this);
837 alListenerfv(AL_VELOCITY, vel);
840 void ALContext::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
842 CheckContext(this);
843 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
844 alListenerfv(AL_ORIENTATION, ori);
847 void ALContext::setOrientation(const ALfloat *at, const ALfloat *up)
849 CheckContext(this);
850 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
851 alListenerfv(AL_ORIENTATION, ori);
854 void ALContext::setOrientation(const ALfloat *ori)
856 CheckContext(this);
857 alListenerfv(AL_ORIENTATION, ori);
860 void ALContext::setMetersPerUnit(ALfloat m_u)
862 if(!(m_u > 0.0f))
863 throw std::runtime_error("Invalid meters per unit");
864 CheckContext(this);
865 if(hasExtension(EXT_EFX))
866 alListenerf(AL_METERS_PER_UNIT, m_u);
870 void Context::MakeCurrent(Context *context)
872 ALContext *ctx = nullptr;
873 if(context)
875 ctx = cast<ALContext*>(context);
876 if(!ctx) throw std::runtime_error("Invalid context pointer");
878 ALContext::MakeCurrent(ctx);
881 Context *Context::GetCurrent()
883 return ALContext::GetCurrent();
886 void Context::MakeThreadCurrent(Context *context)
888 ALContext *ctx = nullptr;
889 if(context)
891 ctx = cast<ALContext*>(context);
892 if(!ctx) throw std::runtime_error("Invalid context pointer");
894 ALContext::MakeThreadCurrent(ctx);
897 Context *Context::GetThreadCurrent()
899 return ALContext::GetThreadCurrent();