Wake the background thread when updating a streaming source too
[alure.git] / src / context.cpp
blob837554cd7ee0ab168e15e57ccb26c30aeef97c84
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>
15 #include "alc.h"
17 #ifdef HAVE_VORBISFILE
18 #include "decoders/vorbisfile.hpp"
19 #endif
20 #ifdef HAVE_LIBFLAC
21 #include "decoders/flac.hpp"
22 #endif
23 #ifdef HAVE_OPUSFILE
24 #include "decoders/opusfile.hpp"
25 #endif
26 #ifdef HAVE_LIBSNDFILE
27 #include "decoders/sndfile.hpp"
28 #endif
29 #ifdef HAVE_MPG123
30 #include "decoders/mpg123.hpp"
31 #endif
32 #include "decoders/wave.hpp"
34 #include "devicemanager.h"
35 #include "device.h"
36 #include "buffer.h"
37 #include "source.h"
38 #include "auxeffectslot.h"
39 #include "effect.h"
41 namespace alure
44 template<typename T, size_t N>
45 static inline size_t countof(const T(&)[N])
46 { return N; }
49 typedef std::pair<std::string,SharedPtr<DecoderFactory>> FactoryPair;
50 static const FactoryPair sDefaultDecoders[] = {
51 #ifdef HAVE_VORBISFILE
52 { "_alure_int_vorbis", SharedPtr<DecoderFactory>(new VorbisFileDecoderFactory) },
53 #endif
54 #ifdef HAVE_LIBFLAC
55 { "_alure_int_flac", SharedPtr<DecoderFactory>(new FlacDecoderFactory) },
56 #endif
57 #ifdef HAVE_OPUSFILE
58 { "_alure_int_opus", SharedPtr<DecoderFactory>(new OpusFileDecoderFactory) },
59 #endif
61 { "_alure_int_wave", SharedPtr<DecoderFactory>(new WaveDecoderFactory) },
63 #ifdef HAVE_LIBSNDFILE
64 { "_alure_int_sndfile", SharedPtr<DecoderFactory>(new SndFileDecoderFactory) },
65 #endif
66 #ifdef HAVE_MPG123
67 { "_alure_int_mpg123", SharedPtr<DecoderFactory>(new Mpg123DecoderFactory) },
68 #endif
71 typedef std::map<std::string,SharedPtr<DecoderFactory>> FactoryMap;
72 static FactoryMap sDecoders;
74 template<typename T>
75 static SharedPtr<Decoder> GetDecoder(const std::string &name, SharedPtr<std::istream> file, T start, T end)
77 while(start != end)
79 DecoderFactory *factory = start->second.get();
80 SharedPtr<Decoder> decoder = factory->createDecoder(file);
81 if(decoder) return decoder;
83 file->clear();
84 if(!file->seekg(0))
85 throw std::runtime_error("Failed to rewind "+name+" for the next decoder factory");
87 ++start;
90 return SharedPtr<Decoder>(nullptr);
93 void RegisterDecoder(const std::string &name, SharedPtr<DecoderFactory> factory)
95 FactoryMap::iterator iter = sDecoders.begin();
96 while(iter != sDecoders.end())
98 if(iter->first == name)
99 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
100 if(iter->second.get() == factory.get())
102 std::stringstream sstr;
103 sstr<< "Decoder factory instance "<<factory<<" already registered";
104 throw std::runtime_error(sstr.str());
106 iter++;
108 sDecoders.insert(std::make_pair(name, factory));
111 SharedPtr<DecoderFactory> UnregisterDecoder(const std::string &name)
113 FactoryMap::iterator iter = sDecoders.find(name);
114 if(iter != sDecoders.end())
116 SharedPtr<DecoderFactory> factory = iter->second;
117 sDecoders.erase(iter);
118 return factory;
120 return SharedPtr<DecoderFactory>(nullptr);
124 class DefaultFileIOFactory : public FileIOFactory {
125 virtual SharedPtr<std::istream> openFile(const std::string &name)
127 SharedPtr<std::ifstream> file(new std::ifstream(name.c_str(), std::ios::binary));
128 if(!file->is_open()) file.reset();
129 return file;
132 static DefaultFileIOFactory sDefaultFileFactory;
134 static SharedPtr<FileIOFactory> sFileFactory;
135 SharedPtr<FileIOFactory> FileIOFactory::set(SharedPtr<FileIOFactory> factory)
137 sFileFactory.swap(factory);
138 return factory;
141 FileIOFactory &FileIOFactory::get()
143 FileIOFactory *factory = sFileFactory.get();
144 if(factory) return *factory;
145 return sDefaultFileFactory;
149 template<typename T>
150 static inline void LoadALFunc(T **func, const char *name)
151 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
154 static void LoadNothing(ALContext*) { }
156 static void LoadEFX(ALContext *ctx)
158 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
159 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
160 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
161 LoadALFunc(&ctx->alEffecti, "alEffecti");
162 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
163 LoadALFunc(&ctx->alEffectf, "alEffectf");
164 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
165 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
166 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
167 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
168 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
170 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
171 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
172 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
173 LoadALFunc(&ctx->alFilteri, "alFilteri");
174 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
175 LoadALFunc(&ctx->alFilterf, "alFilterf");
176 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
177 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
178 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
179 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
180 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
182 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
183 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
184 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
185 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
186 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
187 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
188 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
189 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
190 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
191 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
192 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
195 static void LoadSourceLatency(ALContext *ctx)
197 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
200 static const struct {
201 enum ALExtension extension;
202 const char name[32];
203 void (*loader)(ALContext*);
204 } ALExtensionList[] = {
205 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
207 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
208 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
209 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
211 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
212 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
213 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
215 { SOFT_loop_points, "AL_SOFT_loop_points", LoadNothing },
216 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
220 ALContext *ALContext::sCurrentCtx = 0;
221 thread_local ALContext *ALContext::sThreadCurrentCtx;
223 void ALContext::MakeCurrent(ALContext *context)
225 std::unique_lock<std::mutex> lock1, lock2;
226 if(sCurrentCtx)
227 lock1 = std::unique_lock<std::mutex>(sCurrentCtx->mMutex);
228 if(context && context != sCurrentCtx)
229 lock2 = std::unique_lock<std::mutex>(context->mMutex);
231 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
232 throw std::runtime_error("Call to alcMakeContextCurrent failed");
233 if(context)
235 context->addRef();
236 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
238 if(sCurrentCtx)
239 sCurrentCtx->decRef();
240 sCurrentCtx = context;
241 if(sThreadCurrentCtx)
242 sThreadCurrentCtx->decRef();
243 sThreadCurrentCtx = 0;
245 if(sCurrentCtx)
247 lock2.unlock();
248 sCurrentCtx->mWakeThread.notify_all();
252 void ALContext::MakeThreadCurrent(ALContext *context)
254 if(!ALDeviceManager::SetThreadContext)
255 throw std::runtime_error("Thread-local contexts unsupported");
256 if(ALDeviceManager::SetThreadContext(context ? context->getContext() : 0) == ALC_FALSE)
257 throw std::runtime_error("Call to alcSetThreadContext failed");
258 if(context)
260 context->addRef();
261 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
263 if(sThreadCurrentCtx)
264 sThreadCurrentCtx->decRef();
265 sThreadCurrentCtx = context;
268 void ALContext::setupExts()
270 ALCdevice *device = mDevice->getDevice();
271 for(size_t i = 0;i < countof(ALExtensionList);i++)
273 mHasExt[ALExtensionList[i].extension] = false;
274 if(strncmp(ALExtensionList[i].name, "ALC", 3) == 0)
275 mHasExt[ALExtensionList[i].extension] = alcIsExtensionPresent(device, ALExtensionList[i].name);
276 else
277 mHasExt[ALExtensionList[i].extension] = alIsExtensionPresent(ALExtensionList[i].name);
279 if(mHasExt[ALExtensionList[i].extension])
280 ALExtensionList[i].loader(this);
285 void ALContext::backgroundProc()
287 if(ALDeviceManager::SetThreadContext && mDevice->hasExtension(EXT_thread_local_context))
288 ALDeviceManager::SetThreadContext(getContext());
290 std::unique_lock<std::mutex> lock(mMutex);
291 while(!mQuitThread)
293 for(PendingBuffer &pendbuf : mPendingBuffers)
294 pendbuf.mBuffer->load(pendbuf.mFrames, pendbuf.mFormat,
295 pendbuf.mDecoder, pendbuf.mName, this);
296 mPendingBuffers.clear();
298 auto source = mStreamingSources.begin();
299 while(source != mStreamingSources.end())
301 if(!(*source)->updateAsync())
302 source = mStreamingSources.erase(source);
303 else
304 ++source;
307 do {
308 mWakeThread.wait(lock);
309 } while(!mQuitThread && alcGetCurrentContext() != getContext());
311 lock.unlock();
313 if(ALDeviceManager::SetThreadContext)
314 ALDeviceManager::SetThreadContext(0);
318 ALContext::ALContext(ALCcontext *context, ALDevice *device)
319 : mContext(context), mDevice(device), mRefs(0),
320 mHasExt{false}, mQuitThread(false),
321 alGetSourcei64vSOFT(0),
322 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
323 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
324 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
325 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
326 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
327 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
328 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
329 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
330 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
334 ALContext::~ALContext()
336 mDevice->removeContext(this);
340 Device *ALContext::getDevice()
342 return mDevice;
345 void ALContext::destroy()
347 if(mRefs.load() != 0)
348 throw std::runtime_error("Context is in use");
350 if(mThread.joinable())
352 std::unique_lock<std::mutex> lock(mMutex);
353 mQuitThread = true;
354 lock.unlock();
355 mWakeThread.notify_all();
356 mThread.join();
359 alcDestroyContext(mContext);
360 mContext = 0;
361 delete this;
365 void ALContext::startBatch()
367 alcSuspendContext(mContext);
370 void ALContext::endBatch()
372 alcProcessContext(mContext);
376 Listener *ALContext::getListener()
378 return this;
382 SharedPtr<MessageHandler> ALContext::setMessageHandler(SharedPtr<MessageHandler> handler)
384 std::lock_guard<std::mutex> lock(mMutex);
385 mMessage.swap(handler);
386 return handler;
389 SharedPtr<MessageHandler> ALContext::getMessageHandler() const
391 return mMessage;
395 SharedPtr<Decoder> ALContext::createDecoder(const std::string &name)
397 SharedPtr<std::istream> file(FileIOFactory::get().openFile(name));
398 if(!file.get()) throw std::runtime_error("Failed to open "+name);
400 SharedPtr<Decoder> decoder = GetDecoder(name, file, sDecoders.begin(), sDecoders.end());
401 if(!decoder) decoder = GetDecoder(name, file, std::begin(sDefaultDecoders), std::end(sDefaultDecoders));
402 if(decoder) return decoder;
404 throw std::runtime_error("No decoder for "+name);
408 Buffer *ALContext::getBuffer(const std::string &name)
410 CheckContext(this);
412 Buffer *buffer = mDevice->getBuffer(name);
413 if(buffer)
415 // Ensure the buffer is loaded before returning. getBuffer guarantees
416 // the returned buffer is loaded.
417 while(buffer->getLoadStatus() == BufferLoad_Pending)
418 std::this_thread::yield();
419 return buffer;
422 SharedPtr<Decoder> decoder(createDecoder(name));
424 ALuint srate = decoder->getFrequency();
425 ChannelConfig chans = decoder->getChannelConfig();
426 SampleType type = decoder->getSampleType();
427 ALuint frames = decoder->getLength();
429 std::vector<ALbyte> data(FramesToBytes(frames, chans, type));
430 frames = decoder->read(&data[0], frames);
431 if(!frames) throw std::runtime_error("No samples for buffer");
432 data.resize(FramesToBytes(frames, chans, type));
434 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
435 if(loop_pts.first >= loop_pts.second)
436 loop_pts = std::make_pair(0, frames);
437 else
439 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
440 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
443 // Get the format before calling the bufferLoading message handler, to
444 // ensure it's something OpenAL can handle.
445 ALenum format = GetFormat(chans, type);
447 if(mMessage.get())
448 mMessage->bufferLoading(name, chans, type, srate, data);
450 alGetError();
451 ALuint bid = 0;
452 try {
453 alGenBuffers(1, &bid);
454 alBufferData(bid, format, &data[0], data.size(), srate);
455 if(hasExtension(SOFT_loop_points))
457 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
458 alBufferiv(bid, AL_LOOP_POINTS_SOFT, pts);
460 if(alGetError() != AL_NO_ERROR)
461 throw std::runtime_error("Failed to buffer data");
463 return mDevice->addBuffer(name, new ALBuffer(mDevice, bid, srate, chans, type, true));
465 catch(...) {
466 alDeleteBuffers(1, &bid);
467 throw;
471 Buffer *ALContext::getBufferAsync(const std::string &name)
473 CheckContext(this);
475 Buffer *buffer = mDevice->getBuffer(name);
476 if(buffer) return buffer;
478 SharedPtr<Decoder> decoder(createDecoder(name));
480 ALuint srate = decoder->getFrequency();
481 ChannelConfig chans = decoder->getChannelConfig();
482 SampleType type = decoder->getSampleType();
483 ALuint frames = decoder->getLength();
484 if(!frames) throw std::runtime_error("No samples for buffer");
486 ALenum format = GetFormat(chans, type);
488 alGetError();
489 ALuint bid = 0;
490 alGenBuffers(1, &bid);
491 if(alGetError() != AL_NO_ERROR)
492 throw std::runtime_error("Failed to buffer data");
494 ALBuffer *newbuf = new ALBuffer(mDevice, bid, srate, chans, type, false);
496 std::unique_lock<std::mutex> lock(mMutex);
497 if(mThread.get_id() == std::thread::id())
498 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
499 mPendingBuffers.push_back(PendingBuffer{name, newbuf, decoder, format, frames});
500 lock.unlock();
501 mWakeThread.notify_all();
503 return mDevice->addBuffer(name, newbuf);
507 void ALContext::removeBuffer(const std::string &name)
509 CheckContext(this);
510 mDevice->removeBuffer(name);
513 void ALContext::removeBuffer(Buffer *buffer)
515 CheckContext(this);
516 mDevice->removeBuffer(buffer);
520 ALuint ALContext::getSourceId(ALuint maxprio)
522 CheckContext(this);
524 ALuint id = 0;
525 if(mSourceIds.empty())
527 alGetError();
528 alGenSources(1, &id);
529 if(alGetError() == AL_NO_ERROR)
530 return id;
532 ALSource *lowest = 0;
533 for(ALSource *src : mUsedSources)
535 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
536 lowest = src;
538 if(lowest && lowest->getPriority() < maxprio)
539 lowest->stop();
541 if(mSourceIds.empty())
542 throw std::runtime_error("No available sources");
544 id = mSourceIds.top();
545 mSourceIds.pop();
546 return id;
550 Source *ALContext::getSource()
552 CheckContext(this);
554 ALSource *source = 0;
555 if(mFreeSources.empty())
556 source = new ALSource(this);
557 else
559 source = mFreeSources.back();
560 mFreeSources.pop();
562 mUsedSources.insert(source);
563 return source;
566 void ALContext::freeSource(ALSource *source)
568 mUsedSources.erase(source);
569 mFreeSources.push(source);
573 void ALContext::addStream(ALSource *source)
575 std::lock_guard<std::mutex> lock(mMutex);
576 if(mThread.get_id() == std::thread::id())
577 mThread = std::thread(std::mem_fn(&ALContext::backgroundProc), this);
578 mStreamingSources.insert(source);
581 void ALContext::removeStream(ALSource *source)
583 std::lock_guard<std::mutex> lock(mMutex);
584 mStreamingSources.erase(source);
588 AuxiliaryEffectSlot *ALContext::createAuxiliaryEffectSlot()
590 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
591 throw std::runtime_error("AuxiliaryEffectSlots not supported");
592 CheckContext(this);
594 alGetError();
595 ALuint id = 0;
596 alGenAuxiliaryEffectSlots(1, &id);
597 if(alGetError() != AL_NO_ERROR)
598 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
599 try {
600 return new ALAuxiliaryEffectSlot(this, id);
602 catch(...) {
603 alDeleteAuxiliaryEffectSlots(1, &id);
604 throw;
609 Effect *ALContext::createEffect()
611 if(!hasExtension(EXT_EFX))
612 throw std::runtime_error("Effects not supported");
613 CheckContext(this);
615 alGetError();
616 ALuint id = 0;
617 alGenEffects(1, &id);
618 if(alGetError() != AL_NO_ERROR)
619 throw std::runtime_error("Failed to create Effect");
620 try {
621 return new ALEffect(this, id);
623 catch(...) {
624 alDeleteEffects(1, &id);
625 throw;
630 void ALContext::setDopplerFactor(ALfloat factor)
632 if(!(factor >= 0.0f))
633 throw std::runtime_error("Doppler factor out of range");
634 CheckContext(this);
635 alDopplerFactor(factor);
639 void ALContext::setSpeedOfSound(ALfloat speed)
641 if(!(speed > 0.0f))
642 throw std::runtime_error("Speed of sound out of range");
643 CheckContext(this);
644 alSpeedOfSound(speed);
648 void ALContext::setDistanceModel(DistanceModel model)
650 CheckContext(this);
651 alDistanceModel(model);
655 void ALContext::update()
657 CheckContext(this);
658 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fun(&ALSource::updateNoCtxCheck));
659 // For performance reasons, don't wait for the thread's mutex. This should
660 // be called often enough to keep up with any and all streams regardless.
661 wakeThread();
665 void ALContext::setGain(ALfloat gain)
667 if(!(gain >= 0.0f))
668 throw std::runtime_error("Gain out of range");
669 CheckContext(this);
670 alListenerf(AL_GAIN, gain);
674 void ALContext::setPosition(ALfloat x, ALfloat y, ALfloat z)
676 CheckContext(this);
677 alListener3f(AL_POSITION, x, y, z);
680 void ALContext::setPosition(const ALfloat *pos)
682 CheckContext(this);
683 alListenerfv(AL_POSITION, pos);
686 void ALContext::setVelocity(ALfloat x, ALfloat y, ALfloat z)
688 CheckContext(this);
689 alListener3f(AL_VELOCITY, x, y, z);
692 void ALContext::setVelocity(const ALfloat *vel)
694 CheckContext(this);
695 alListenerfv(AL_VELOCITY, vel);
698 void ALContext::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
700 CheckContext(this);
701 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
702 alListenerfv(AL_ORIENTATION, ori);
705 void ALContext::setOrientation(const ALfloat *at, const ALfloat *up)
707 CheckContext(this);
708 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
709 alListenerfv(AL_ORIENTATION, ori);
712 void ALContext::setOrientation(const ALfloat *ori)
714 CheckContext(this);
715 alListenerfv(AL_ORIENTATION, ori);
719 void Context::MakeCurrent(Context *context)
721 ALContext *ctx = 0;
722 if(context)
724 ctx = cast<ALContext*>(context);
725 if(!ctx) throw std::runtime_error("Invalid context pointer");
727 ALContext::MakeCurrent(ctx);
730 Context *Context::GetCurrent()
732 return ALContext::GetCurrent();
735 void Context::MakeThreadCurrent(Context *context)
737 ALContext *ctx = 0;
738 if(context)
740 ctx = cast<ALContext*>(context);
741 if(!ctx) throw std::runtime_error("Invalid context pointer");
743 ALContext::MakeThreadCurrent(ctx);
746 Context *Context::GetThreadCurrent()
748 return ALContext::GetThreadCurrent();