17 #ifdef HAVE_VORBISFILE
18 #include "decoders/vorbisfile.hpp"
21 #include "decoders/flac.hpp"
24 #include "decoders/opusfile.hpp"
26 #ifdef HAVE_LIBSNDFILE
27 #include "decoders/sndfile.hpp"
30 #include "decoders/mpg123.hpp"
32 #include "decoders/wave.hpp"
34 #include "devicemanager.h"
38 #include "auxeffectslot.h"
44 template<typename T
, size_t N
>
45 static inline size_t countof(const T(&)[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
) },
55 { "_alure_int_flac", SharedPtr
<DecoderFactory
>(new FlacDecoderFactory
) },
58 { "_alure_int_opus", SharedPtr
<DecoderFactory
>(new OpusFileDecoderFactory
) },
61 { "_alure_int_wave", SharedPtr
<DecoderFactory
>(new WaveDecoderFactory
) },
63 #ifdef HAVE_LIBSNDFILE
64 { "_alure_int_sndfile", SharedPtr
<DecoderFactory
>(new SndFileDecoderFactory
) },
67 { "_alure_int_mpg123", SharedPtr
<DecoderFactory
>(new Mpg123DecoderFactory
) },
71 typedef std::map
<std::string
,SharedPtr
<DecoderFactory
>> FactoryMap
;
72 static FactoryMap sDecoders
;
75 static SharedPtr
<Decoder
> GetDecoder(const std::string
&name
, SharedPtr
<std::istream
> file
, T start
, T end
)
79 DecoderFactory
*factory
= start
->second
.get();
80 SharedPtr
<Decoder
> decoder
= factory
->createDecoder(file
);
81 if(decoder
) return decoder
;
85 throw std::runtime_error("Failed to rewind "+name
+" for the next decoder factory");
90 return SharedPtr
<Decoder
>(nullptr);
93 static SharedPtr
<Decoder
> GetDecoder(const std::string
&name
, SharedPtr
<std::istream
> file
)
95 SharedPtr
<Decoder
> decoder
= GetDecoder(name
, file
, sDecoders
.begin(), sDecoders
.end());
96 if(!decoder
) decoder
= GetDecoder(name
, file
, std::begin(sDefaultDecoders
), std::end(sDefaultDecoders
));
97 if(!decoder
) throw std::runtime_error("No decoder for "+name
);
101 void RegisterDecoder(const std::string
&name
, SharedPtr
<DecoderFactory
> factory
)
103 FactoryMap::iterator iter
= sDecoders
.begin();
104 while(iter
!= sDecoders
.end())
106 if(iter
->first
== name
)
107 throw std::runtime_error("Decoder factory \""+name
+"\" already registered");
108 if(iter
->second
.get() == factory
.get())
110 std::stringstream sstr
;
111 sstr
<< "Decoder factory instance "<<factory
<<" already registered";
112 throw std::runtime_error(sstr
.str());
116 sDecoders
.insert(std::make_pair(name
, factory
));
119 SharedPtr
<DecoderFactory
> UnregisterDecoder(const std::string
&name
)
121 FactoryMap::iterator iter
= sDecoders
.find(name
);
122 if(iter
!= sDecoders
.end())
124 SharedPtr
<DecoderFactory
> factory
= iter
->second
;
125 sDecoders
.erase(iter
);
128 return SharedPtr
<DecoderFactory
>(nullptr);
132 class DefaultFileIOFactory
: public FileIOFactory
{
133 virtual SharedPtr
<std::istream
> openFile(const std::string
&name
)
135 SharedPtr
<std::ifstream
> file(new std::ifstream(name
.c_str(), std::ios::binary
));
136 if(!file
->is_open()) file
.reset();
140 static DefaultFileIOFactory sDefaultFileFactory
;
142 static SharedPtr
<FileIOFactory
> sFileFactory
;
143 SharedPtr
<FileIOFactory
> FileIOFactory::set(SharedPtr
<FileIOFactory
> factory
)
145 sFileFactory
.swap(factory
);
149 FileIOFactory
&FileIOFactory::get()
151 FileIOFactory
*factory
= sFileFactory
.get();
152 if(factory
) return *factory
;
153 return sDefaultFileFactory
;
158 static inline void LoadALFunc(T
**func
, const char *name
)
159 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
162 static void LoadNothing(ALContext
*) { }
164 static void LoadEFX(ALContext
*ctx
)
166 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
167 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
168 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
169 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
170 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
171 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
172 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
173 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
174 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
175 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
176 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
178 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
179 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
180 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
181 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
182 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
183 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
184 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
185 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
186 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
187 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
188 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
190 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
191 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
192 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
193 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
194 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
195 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
196 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
197 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
198 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
199 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
200 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
203 static void LoadSourceLatency(ALContext
*ctx
)
205 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
208 static const struct {
209 enum ALExtension extension
;
211 void (*loader
)(ALContext
*);
212 } ALExtensionList
[] = {
213 { EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
215 { EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
216 { EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
217 { EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
219 { EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
220 { EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
221 { EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
223 { SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
224 { SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
228 ALContext
*ALContext::sCurrentCtx
= 0;
229 thread_local ALContext
*ALContext::sThreadCurrentCtx
;
231 void ALContext::MakeCurrent(ALContext
*context
)
233 std::unique_lock
<std::mutex
> lock1
, lock2
;
235 lock1
= std::unique_lock
<std::mutex
>(sCurrentCtx
->mMutex
);
236 if(context
&& context
!= sCurrentCtx
)
237 lock2
= std::unique_lock
<std::mutex
>(context
->mMutex
);
239 if(alcMakeContextCurrent(context
? context
->getContext() : 0) == ALC_FALSE
)
240 throw std::runtime_error("Call to alcMakeContextCurrent failed");
244 std::call_once(context
->mSetExts
, std::mem_fn(&ALContext::setupExts
), context
);
246 std::swap(sCurrentCtx
, context
);
247 if(context
) context
->decRef();
249 if(sThreadCurrentCtx
)
250 sThreadCurrentCtx
->decRef();
251 sThreadCurrentCtx
= 0;
253 if(sCurrentCtx
&& sCurrentCtx
!= context
)
256 sCurrentCtx
->mWakeThread
.notify_all();
260 void ALContext::MakeThreadCurrent(ALContext
*context
)
262 if(!ALDeviceManager::SetThreadContext
)
263 throw std::runtime_error("Thread-local contexts unsupported");
264 if(ALDeviceManager::SetThreadContext(context
? context
->getContext() : 0) == ALC_FALSE
)
265 throw std::runtime_error("Call to alcSetThreadContext failed");
269 std::call_once(context
->mSetExts
, std::mem_fn(&ALContext::setupExts
), context
);
271 if(sThreadCurrentCtx
)
272 sThreadCurrentCtx
->decRef();
273 sThreadCurrentCtx
= context
;
276 void ALContext::setupExts()
278 ALCdevice
*device
= mDevice
->getDevice();
279 for(size_t i
= 0;i
< countof(ALExtensionList
);i
++)
281 mHasExt
[ALExtensionList
[i
].extension
] = false;
282 if(strncmp(ALExtensionList
[i
].name
, "ALC", 3) == 0)
283 mHasExt
[ALExtensionList
[i
].extension
] = alcIsExtensionPresent(device
, ALExtensionList
[i
].name
);
285 mHasExt
[ALExtensionList
[i
].extension
] = alIsExtensionPresent(ALExtensionList
[i
].name
);
287 if(mHasExt
[ALExtensionList
[i
].extension
])
288 ALExtensionList
[i
].loader(this);
293 void ALContext::backgroundProc()
295 if(ALDeviceManager::SetThreadContext
&& mDevice
->hasExtension(EXT_thread_local_context
))
296 ALDeviceManager::SetThreadContext(getContext());
298 std::unique_lock
<std::mutex
> lock(mMutex
);
301 for(PendingBuffer
&pendbuf
: mPendingBuffers
)
302 pendbuf
.mBuffer
->load(pendbuf
.mFrames
, pendbuf
.mFormat
,
303 pendbuf
.mDecoder
, pendbuf
.mName
, this);
304 mPendingBuffers
.clear();
306 auto source
= mStreamingSources
.begin();
307 while(source
!= mStreamingSources
.end())
309 if(!(*source
)->updateAsync())
310 source
= mStreamingSources
.erase(source
);
316 mWakeThread
.wait(lock
);
317 } while(!mQuitThread
&& alcGetCurrentContext() != getContext());
321 if(ALDeviceManager::SetThreadContext
)
322 ALDeviceManager::SetThreadContext(0);
326 ALContext::ALContext(ALCcontext
*context
, ALDevice
*device
)
327 : mContext(context
), mDevice(device
), mRefs(0),
328 mHasExt
{false}, mQuitThread(false),
329 alGetSourcei64vSOFT(0),
330 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
331 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
332 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
333 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
334 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
335 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
336 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
337 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
338 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
342 ALContext::~ALContext()
344 mDevice
->removeContext(this);
348 Device
*ALContext::getDevice()
353 void ALContext::destroy()
355 if(mRefs
.load() != 0)
356 throw std::runtime_error("Context is in use");
357 if(!mBuffers
.empty())
358 throw std::runtime_error("Trying to destroy a context with buffers");
360 if(mThread
.joinable())
362 std::unique_lock
<std::mutex
> lock(mMutex
);
365 mWakeThread
.notify_all();
369 alcDestroyContext(mContext
);
375 void ALContext::startBatch()
377 alcSuspendContext(mContext
);
380 void ALContext::endBatch()
382 alcProcessContext(mContext
);
386 Listener
*ALContext::getListener()
392 SharedPtr
<MessageHandler
> ALContext::setMessageHandler(SharedPtr
<MessageHandler
> handler
)
394 std::lock_guard
<std::mutex
> lock(mMutex
);
395 mMessage
.swap(handler
);
399 SharedPtr
<MessageHandler
> ALContext::getMessageHandler() const
405 SharedPtr
<Decoder
> ALContext::createDecoder(const std::string
&name
)
407 SharedPtr
<std::istream
> file(FileIOFactory::get().openFile(name
));
408 if(file
.get()) return GetDecoder(name
, file
);
410 // Resource not found. Try to find a substitute.
411 if(!mMessage
.get()) throw std::runtime_error("Failed to open "+name
);
412 std::string oldname
= name
;
415 if(!mMessage
->resourceNotFound(oldname
, newname
))
416 throw std::runtime_error("Failed to open "+oldname
);
417 file
= FileIOFactory::get().openFile(newname
);
418 oldname
= std::move(newname
);
419 } while(!file
.get());
421 return GetDecoder(oldname
, file
);
425 Buffer
*ALContext::getBuffer(const std::string
&name
)
429 BufferMap::const_iterator iter
= mBuffers
.find(name
);
430 if(iter
!= mBuffers
.end())
432 // Ensure the buffer is loaded before returning. getBuffer guarantees
433 // the returned buffer is loaded.
434 ALBuffer
*buffer
= iter
->second
;
435 while(buffer
->getLoadStatus() == BufferLoad_Pending
)
436 std::this_thread::yield();
440 SharedPtr
<Decoder
> decoder(createDecoder(name
));
442 ALuint srate
= decoder
->getFrequency();
443 ChannelConfig chans
= decoder
->getChannelConfig();
444 SampleType type
= decoder
->getSampleType();
445 ALuint frames
= decoder
->getLength();
447 std::vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
448 frames
= decoder
->read(&data
[0], frames
);
449 if(!frames
) throw std::runtime_error("No samples for buffer");
450 data
.resize(FramesToBytes(frames
, chans
, type
));
452 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
453 if(loop_pts
.first
>= loop_pts
.second
)
454 loop_pts
= std::make_pair(0, frames
);
457 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
458 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
461 // Get the format before calling the bufferLoading message handler, to
462 // ensure it's something OpenAL can handle.
463 ALenum format
= GetFormat(chans
, type
);
466 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
471 alGenBuffers(1, &bid
);
472 alBufferData(bid
, format
, &data
[0], data
.size(), srate
);
473 if(hasExtension(SOFT_loop_points
))
475 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
476 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
478 if(alGetError() != AL_NO_ERROR
)
479 throw std::runtime_error("Failed to buffer data");
481 ALBuffer
*buffer
= new ALBuffer(mDevice
, bid
, srate
, chans
, type
, true);
482 return mBuffers
.insert(std::make_pair(name
, buffer
)).first
->second
;
485 alDeleteBuffers(1, &bid
);
490 Buffer
*ALContext::getBufferAsync(const std::string
&name
)
494 BufferMap::const_iterator iter
= mBuffers
.find(name
);
495 if(iter
!= mBuffers
.end()) return iter
->second
;
497 SharedPtr
<Decoder
> decoder(createDecoder(name
));
499 ALuint srate
= decoder
->getFrequency();
500 ChannelConfig chans
= decoder
->getChannelConfig();
501 SampleType type
= decoder
->getSampleType();
502 ALuint frames
= decoder
->getLength();
503 if(!frames
) throw std::runtime_error("No samples for buffer");
505 ALenum format
= GetFormat(chans
, type
);
509 alGenBuffers(1, &bid
);
510 if(alGetError() != AL_NO_ERROR
)
511 throw std::runtime_error("Failed to buffer data");
513 ALBuffer
*buffer
= new ALBuffer(mDevice
, bid
, srate
, chans
, type
, false);
515 std::unique_lock
<std::mutex
> lock(mMutex
);
516 if(mThread
.get_id() == std::thread::id())
517 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
518 mPendingBuffers
.push_back(PendingBuffer
{name
, buffer
, decoder
, format
, frames
});
520 mWakeThread
.notify_all();
522 return mBuffers
.insert(std::make_pair(name
, buffer
)).first
->second
;
526 void ALContext::removeBuffer(const std::string
&name
)
529 BufferMap::iterator iter
= mBuffers
.find(name
);
530 if(iter
!= mBuffers
.end())
532 ALBuffer
*albuf
= iter
->second
;
534 mBuffers
.erase(iter
);
538 void ALContext::removeBuffer(Buffer
*buffer
)
541 BufferMap::iterator iter
= mBuffers
.begin();
542 while(iter
!= mBuffers
.end())
544 ALBuffer
*albuf
= iter
->second
;
548 mBuffers
.erase(iter
);
556 ALuint
ALContext::getSourceId(ALuint maxprio
)
561 if(mSourceIds
.empty())
564 alGenSources(1, &id
);
565 if(alGetError() == AL_NO_ERROR
)
568 ALSource
*lowest
= 0;
569 for(ALSource
*src
: mUsedSources
)
571 if(src
->getId() != 0 && (!lowest
|| src
->getPriority() < lowest
->getPriority()))
574 if(lowest
&& lowest
->getPriority() < maxprio
)
577 if(mSourceIds
.empty())
578 throw std::runtime_error("No available sources");
580 id
= mSourceIds
.top();
586 Source
*ALContext::getSource()
590 ALSource
*source
= 0;
591 if(mFreeSources
.empty())
592 source
= new ALSource(this);
595 source
= mFreeSources
.back();
598 mUsedSources
.insert(source
);
602 void ALContext::freeSource(ALSource
*source
)
604 mUsedSources
.erase(source
);
605 mFreeSources
.push(source
);
609 void ALContext::addStream(ALSource
*source
)
611 std::lock_guard
<std::mutex
> lock(mMutex
);
612 if(mThread
.get_id() == std::thread::id())
613 mThread
= std::thread(std::mem_fn(&ALContext::backgroundProc
), this);
614 mStreamingSources
.insert(source
);
617 void ALContext::removeStream(ALSource
*source
)
619 std::lock_guard
<std::mutex
> lock(mMutex
);
620 mStreamingSources
.erase(source
);
624 AuxiliaryEffectSlot
*ALContext::createAuxiliaryEffectSlot()
626 if(!hasExtension(EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
627 throw std::runtime_error("AuxiliaryEffectSlots not supported");
632 alGenAuxiliaryEffectSlots(1, &id
);
633 if(alGetError() != AL_NO_ERROR
)
634 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
636 return new ALAuxiliaryEffectSlot(this, id
);
639 alDeleteAuxiliaryEffectSlots(1, &id
);
645 Effect
*ALContext::createEffect()
647 if(!hasExtension(EXT_EFX
))
648 throw std::runtime_error("Effects not supported");
653 alGenEffects(1, &id
);
654 if(alGetError() != AL_NO_ERROR
)
655 throw std::runtime_error("Failed to create Effect");
657 return new ALEffect(this, id
);
660 alDeleteEffects(1, &id
);
666 void ALContext::setDopplerFactor(ALfloat factor
)
668 if(!(factor
>= 0.0f
))
669 throw std::runtime_error("Doppler factor out of range");
671 alDopplerFactor(factor
);
675 void ALContext::setSpeedOfSound(ALfloat speed
)
678 throw std::runtime_error("Speed of sound out of range");
680 alSpeedOfSound(speed
);
684 void ALContext::setDistanceModel(DistanceModel model
)
687 alDistanceModel(model
);
691 void ALContext::update()
694 std::for_each(mUsedSources
.begin(), mUsedSources
.end(), std::mem_fun(&ALSource::updateNoCtxCheck
));
695 // For performance reasons, don't wait for the thread's mutex. This should
696 // be called often enough to keep up with any and all streams regardless.
701 void ALContext::setGain(ALfloat gain
)
704 throw std::runtime_error("Gain out of range");
706 alListenerf(AL_GAIN
, gain
);
710 void ALContext::setPosition(ALfloat x
, ALfloat y
, ALfloat z
)
713 alListener3f(AL_POSITION
, x
, y
, z
);
716 void ALContext::setPosition(const ALfloat
*pos
)
719 alListenerfv(AL_POSITION
, pos
);
722 void ALContext::setVelocity(ALfloat x
, ALfloat y
, ALfloat z
)
725 alListener3f(AL_VELOCITY
, x
, y
, z
);
728 void ALContext::setVelocity(const ALfloat
*vel
)
731 alListenerfv(AL_VELOCITY
, vel
);
734 void ALContext::setOrientation(ALfloat x1
, ALfloat y1
, ALfloat z1
, ALfloat x2
, ALfloat y2
, ALfloat z2
)
737 ALfloat ori
[6] = { x1
, y1
, z1
, x2
, y2
, z2
};
738 alListenerfv(AL_ORIENTATION
, ori
);
741 void ALContext::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
744 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
745 alListenerfv(AL_ORIENTATION
, ori
);
748 void ALContext::setOrientation(const ALfloat
*ori
)
751 alListenerfv(AL_ORIENTATION
, ori
);
754 void ALContext::setMetersPerUnit(ALfloat m_u
)
757 throw std::runtime_error("Invalid meters per unit");
759 if(hasExtension(EXT_EFX
))
760 alListenerf(AL_METERS_PER_UNIT
, m_u
);
764 void Context::MakeCurrent(Context
*context
)
769 ctx
= cast
<ALContext
*>(context
);
770 if(!ctx
) throw std::runtime_error("Invalid context pointer");
772 ALContext::MakeCurrent(ctx
);
775 Context
*Context::GetCurrent()
777 return ALContext::GetCurrent();
780 void Context::MakeThreadCurrent(Context
*context
)
785 ctx
= cast
<ALContext
*>(context
);
786 if(!ctx
) throw std::runtime_error("Invalid context pointer");
788 ALContext::MakeThreadCurrent(ctx
);
791 Context
*Context::GetThreadCurrent()
793 return ALContext::GetThreadCurrent();