Use shared pointers instead of unique_ptr
[alure.git] / src / context.cpp
blobb2fb0a0734f989242e1edf2937c1a4f672061ce0
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>
14 #include "alc.h"
16 #ifdef HAVE_LIBSNDFILE
17 #include "decoders/sndfile1.h"
18 #endif
19 #ifdef HAVE_MPG123
20 #include "decoders/mpg123-1.h"
21 #endif
23 #include "devicemanager.h"
24 #include "device.h"
25 #include "buffer.h"
26 #include "source.h"
27 #include "auxeffectslot.h"
28 #include "effect.h"
30 namespace alure
33 template<typename T, size_t N>
34 static inline size_t countof(const T(&)[N])
35 { return N; }
38 typedef std::pair<std::string,SharedPtr<DecoderFactory>> FactoryPair;
39 typedef std::vector<FactoryPair> FactoryMap;
41 static FactoryPair sDefaultDecoders[] = {
42 #ifdef HAVE_MPG123
43 { "_alure_int_mpg123", SharedPtr<DecoderFactory>(new Mpg123DecoderFactory) },
44 #endif
45 #ifdef HAVE_LIBSNDFILE
46 { "_alure_int_sndfile", SharedPtr<DecoderFactory>(new SndFileDecoderFactory) },
47 #endif
49 static FactoryMap sDecoders{ std::make_move_iterator(std::begin(sDefaultDecoders)),
50 std::make_move_iterator(std::end(sDefaultDecoders)) };
52 void RegisterDecoder(const std::string &name, SharedPtr<DecoderFactory> factory)
54 FactoryMap::iterator iter = sDecoders.begin();
55 while(iter != sDecoders.end())
57 if(iter->first == name)
58 throw std::runtime_error("Decoder factory \""+name+"\" already registered");
59 if(iter->second.get() == factory.get())
61 std::stringstream sstr;
62 sstr<< "Decoder factory instance "<<factory<<" already registered";
63 throw std::runtime_error(sstr.str());
65 iter++;
67 sDecoders.push_back(std::make_pair(name, factory));
70 SharedPtr<DecoderFactory> UnregisterDecoder(const std::string &name)
72 FactoryMap::iterator iter = sDecoders.begin();
73 while(iter != sDecoders.end())
75 if(iter->first == name)
77 SharedPtr<DecoderFactory> factory = iter->second;
78 sDecoders.erase(iter);
79 return factory;
81 iter++;
83 return SharedPtr<DecoderFactory>(nullptr);
87 class DefaultFileIOFactory : public FileIOFactory {
88 virtual SharedPtr<std::istream> openFile(const std::string &name)
90 SharedPtr<std::ifstream> file(new std::ifstream(name.c_str(), std::ios::binary));
91 if(!file->is_open()) file.reset();
92 return file;
95 static DefaultFileIOFactory sDefaultFileFactory;
97 static SharedPtr<FileIOFactory> sFileFactory;
98 SharedPtr<FileIOFactory> FileIOFactory::set(SharedPtr<FileIOFactory> factory)
100 SharedPtr<FileIOFactory> old = sFileFactory;
101 sFileFactory = factory;
102 return old;
105 FileIOFactory &FileIOFactory::get()
107 FileIOFactory *factory = sFileFactory.get();
108 if(factory) return *factory;
109 return sDefaultFileFactory;
113 template<typename T>
114 static inline void LoadALFunc(T **func, const char *name)
115 { *func = reinterpret_cast<T*>(alGetProcAddress(name)); }
118 static void LoadNothing(ALContext*) { }
120 static void LoadEFX(ALContext *ctx)
122 LoadALFunc(&ctx->alGenEffects, "alGenEffects");
123 LoadALFunc(&ctx->alDeleteEffects, "alDeleteEffects");
124 LoadALFunc(&ctx->alIsEffect, "alIsEffect");
125 LoadALFunc(&ctx->alEffecti, "alEffecti");
126 LoadALFunc(&ctx->alEffectiv, "alEffectiv");
127 LoadALFunc(&ctx->alEffectf, "alEffectf");
128 LoadALFunc(&ctx->alEffectfv, "alEffectfv");
129 LoadALFunc(&ctx->alGetEffecti, "alGetEffecti");
130 LoadALFunc(&ctx->alGetEffectiv, "alGetEffectiv");
131 LoadALFunc(&ctx->alGetEffectf, "alGetEffectf");
132 LoadALFunc(&ctx->alGetEffectfv, "alGetEffectfv");
134 LoadALFunc(&ctx->alGenFilters, "alGenFilters");
135 LoadALFunc(&ctx->alDeleteFilters, "alDeleteFilters");
136 LoadALFunc(&ctx->alIsFilter, "alIsFilter");
137 LoadALFunc(&ctx->alFilteri, "alFilteri");
138 LoadALFunc(&ctx->alFilteriv, "alFilteriv");
139 LoadALFunc(&ctx->alFilterf, "alFilterf");
140 LoadALFunc(&ctx->alFilterfv, "alFilterfv");
141 LoadALFunc(&ctx->alGetFilteri, "alGetFilteri");
142 LoadALFunc(&ctx->alGetFilteriv, "alGetFilteriv");
143 LoadALFunc(&ctx->alGetFilterf, "alGetFilterf");
144 LoadALFunc(&ctx->alGetFilterfv, "alGetFilterfv");
146 LoadALFunc(&ctx->alGenAuxiliaryEffectSlots, "alGenAuxiliaryEffectSlots");
147 LoadALFunc(&ctx->alDeleteAuxiliaryEffectSlots, "alDeleteAuxiliaryEffectSlots");
148 LoadALFunc(&ctx->alIsAuxiliaryEffectSlot, "alIsAuxiliaryEffectSlot");
149 LoadALFunc(&ctx->alAuxiliaryEffectSloti, "alAuxiliaryEffectSloti");
150 LoadALFunc(&ctx->alAuxiliaryEffectSlotiv, "alAuxiliaryEffectSlotiv");
151 LoadALFunc(&ctx->alAuxiliaryEffectSlotf, "alAuxiliaryEffectSlotf");
152 LoadALFunc(&ctx->alAuxiliaryEffectSlotfv, "alAuxiliaryEffectSlotfv");
153 LoadALFunc(&ctx->alGetAuxiliaryEffectSloti, "alGetAuxiliaryEffectSloti");
154 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotiv, "alGetAuxiliaryEffectSlotiv");
155 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotf, "alGetAuxiliaryEffectSlotf");
156 LoadALFunc(&ctx->alGetAuxiliaryEffectSlotfv, "alGetAuxiliaryEffectSlotfv");
159 static void LoadSourceLatency(ALContext *ctx)
161 LoadALFunc(&ctx->alGetSourcei64vSOFT, "alGetSourcei64vSOFT");
164 static const struct {
165 enum ALExtension extension;
166 const char name[32];
167 void (*loader)(ALContext*);
168 } ALExtensionList[] = {
169 { EXT_EFX, "ALC_EXT_EFX", LoadEFX },
171 { EXT_FLOAT32, "AL_EXT_FLOAT32", LoadNothing },
172 { EXT_MCFORMATS, "AL_EXT_MCFORMATS", LoadNothing },
173 { EXT_BFORMAT, "AL_EXT_BFORMAT", LoadNothing },
175 { EXT_MULAW, "AL_EXT_MULAW", LoadNothing },
176 { EXT_MULAW_MCFORMATS, "AL_EXT_MULAW_MCFORMATS", LoadNothing },
177 { EXT_MULAW_BFORMAT, "AL_EXT_MULAW_BFORMAT", LoadNothing },
179 { SOFT_source_latency, "AL_SOFT_source_latency", LoadSourceLatency },
183 ALContext *ALContext::sCurrentCtx = 0;
184 thread_local ALContext *ALContext::sThreadCurrentCtx;
186 void ALContext::MakeCurrent(ALContext *context)
188 if(alcMakeContextCurrent(context ? context->getContext() : 0) == ALC_FALSE)
189 throw std::runtime_error("Call to alcMakeContextCurrent failed");
190 if(context)
192 context->addRef();
193 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
195 if(sCurrentCtx)
196 sCurrentCtx->decRef();
197 sCurrentCtx = context;
198 if(sThreadCurrentCtx)
199 sThreadCurrentCtx->decRef();
200 sThreadCurrentCtx = 0;
203 void ALContext::MakeThreadCurrent(ALContext *context)
205 if(!ALDeviceManager::SetThreadContext)
206 throw std::runtime_error("Thread-local contexts unsupported");
207 if(ALDeviceManager::SetThreadContext(context ? context->getContext() : 0) == ALC_FALSE)
208 throw std::runtime_error("Call to alcSetThreadContext failed");
209 if(context)
211 context->addRef();
212 std::call_once(context->mSetExts, std::mem_fn(&ALContext::setupExts), context);
214 if(sThreadCurrentCtx)
215 sThreadCurrentCtx->decRef();
216 sThreadCurrentCtx = context;
219 void ALContext::setupExts()
221 ALCdevice *device = mDevice->getDevice();
222 for(size_t i = 0;i < countof(ALExtensionList);i++)
224 mHasExt[ALExtensionList[i].extension] = false;
225 if(strncmp(ALExtensionList[i].name, "ALC", 3) == 0)
226 mHasExt[ALExtensionList[i].extension] = alcIsExtensionPresent(device, ALExtensionList[i].name);
227 else
228 mHasExt[ALExtensionList[i].extension] = alIsExtensionPresent(ALExtensionList[i].name);
230 if(mHasExt[ALExtensionList[i].extension])
231 ALExtensionList[i].loader(this);
236 ALContext::ALContext(ALCcontext *context, ALDevice *device)
237 : mContext(context), mDevice(device), mRefs(0),
238 mHasExt{false},
239 alGetSourcei64vSOFT(0),
240 alGenEffects(0), alDeleteEffects(0), alIsEffect(0),
241 alEffecti(0), alEffectiv(0), alEffectf(0), alEffectfv(0),
242 alGetEffecti(0), alGetEffectiv(0), alGetEffectf(0), alGetEffectfv(0),
243 alGenFilters(0), alDeleteFilters(0), alIsFilter(0),
244 alFilteri(0), alFilteriv(0), alFilterf(0), alFilterfv(0),
245 alGetFilteri(0), alGetFilteriv(0), alGetFilterf(0), alGetFilterfv(0),
246 alGenAuxiliaryEffectSlots(0), alDeleteAuxiliaryEffectSlots(0), alIsAuxiliaryEffectSlot(0),
247 alAuxiliaryEffectSloti(0), alAuxiliaryEffectSlotiv(0), alAuxiliaryEffectSlotf(0), alAuxiliaryEffectSlotfv(0),
248 alGetAuxiliaryEffectSloti(0), alGetAuxiliaryEffectSlotiv(0), alGetAuxiliaryEffectSlotf(0), alGetAuxiliaryEffectSlotfv(0)
252 ALContext::~ALContext()
254 mDevice->removeContext(this);
258 Device *ALContext::getDevice()
260 return mDevice;
263 void ALContext::destroy()
265 if(mRefs.load() != 0)
266 throw std::runtime_error("Context is in use");
268 alcDestroyContext(mContext);
269 mContext = 0;
270 delete this;
274 void ALContext::startBatch()
276 alcSuspendContext(mContext);
279 void ALContext::endBatch()
281 alcProcessContext(mContext);
285 Listener *ALContext::getListener()
287 return this;
291 SharedPtr<Decoder> ALContext::createDecoder(const std::string &name)
293 SharedPtr<std::istream> file(FileIOFactory::get().openFile(name));
294 if(!file.get()) throw std::runtime_error("Failed to open "+name);
296 FactoryMap::const_reverse_iterator iter = sDecoders.rbegin();
297 while(iter != sDecoders.rend())
299 DecoderFactory *factory = iter->second.get();
300 SharedPtr<Decoder> decoder = factory->createDecoder(file);
301 if(decoder.get()) return decoder;
303 file->clear();
304 if(!file->seekg(0))
305 throw std::runtime_error("Failed to rewind "+name+" for the next decoder factory");
307 ++iter;
309 throw std::runtime_error("No decoder for "+name);
313 Buffer *ALContext::getBuffer(const std::string &name)
315 CheckContext(this);
317 Buffer *buffer = mDevice->getBuffer(name);
318 if(buffer) return buffer;
320 SharedPtr<Decoder> decoder(createDecoder(name));
322 ALuint srate = decoder->getFrequency();
323 SampleConfig chans = decoder->getSampleConfig();
324 SampleType type = decoder->getSampleType();
325 ALuint frames = decoder->getLength();
327 std::vector<ALbyte> data(FramesToBytes(frames, chans, type));
328 frames = decoder->read(&data[0], frames);
329 if(!frames) throw std::runtime_error("No samples for buffer");
330 data.resize(FramesToBytes(frames, chans, type));
332 alGetError();
333 ALuint bid = 0;
334 try {
335 alGenBuffers(1, &bid);
336 alBufferData(bid, GetFormat(chans, type), &data[0], data.size(), srate);
337 if(alGetError() != AL_NO_ERROR)
338 throw std::runtime_error("Failed to buffer data");
340 return mDevice->addBuffer(name, new ALBuffer(mDevice, bid, srate, chans, type));
342 catch(...) {
343 alDeleteBuffers(1, &bid);
344 throw;
349 void ALContext::removeBuffer(const std::string &name)
351 CheckContext(this);
352 mDevice->removeBuffer(name);
355 void ALContext::removeBuffer(Buffer *buffer)
357 CheckContext(this);
358 mDevice->removeBuffer(buffer);
362 ALuint ALContext::getSourceId(ALuint maxprio)
364 CheckContext(this);
366 ALuint id = 0;
367 if(mSourceIds.empty())
369 alGetError();
370 alGenSources(1, &id);
371 if(alGetError() == AL_NO_ERROR)
372 return id;
374 ALSource *lowest = 0;
375 for(ALSource *src : mUsedSources)
377 if(src->getId() != 0 && (!lowest || src->getPriority() < lowest->getPriority()))
378 lowest = src;
380 if(lowest && lowest->getPriority() < maxprio)
381 lowest->stop();
383 if(mSourceIds.empty())
384 throw std::runtime_error("No available sources");
386 id = mSourceIds.top();
387 mSourceIds.pop();
388 return id;
392 Source *ALContext::getSource()
394 CheckContext(this);
396 ALSource *source = 0;
397 if(mFreeSources.empty())
398 source = new ALSource(this);
399 else
401 source = mFreeSources.back();
402 mFreeSources.pop();
404 mUsedSources.insert(source);
405 return source;
408 void ALContext::freeSource(ALSource *source)
410 mUsedSources.erase(source);
411 mFreeSources.push(source);
415 AuxiliaryEffectSlot *ALContext::createAuxiliaryEffectSlot()
417 if(!hasExtension(EXT_EFX) || !alGenAuxiliaryEffectSlots)
418 throw std::runtime_error("AuxiliaryEffectSlots not supported");
419 CheckContext(this);
421 alGetError();
422 ALuint id = 0;
423 alGenAuxiliaryEffectSlots(1, &id);
424 if(alGetError() != AL_NO_ERROR)
425 throw std::runtime_error("Failed to create AuxiliaryEffectSlot");
426 try {
427 return new ALAuxiliaryEffectSlot(this, id);
429 catch(...) {
430 alDeleteAuxiliaryEffectSlots(1, &id);
431 throw;
436 Effect *ALContext::createEffect()
438 if(!hasExtension(EXT_EFX))
439 throw std::runtime_error("Effects not supported");
440 CheckContext(this);
442 alGetError();
443 ALuint id = 0;
444 alGenEffects(1, &id);
445 if(alGetError() != AL_NO_ERROR)
446 throw std::runtime_error("Failed to create Effect");
447 try {
448 return new ALEffect(this, id);
450 catch(...) {
451 alDeleteEffects(1, &id);
452 throw;
457 void ALContext::setDopplerFactor(ALfloat factor)
459 if(!(factor >= 0.0f))
460 throw std::runtime_error("Doppler factor out of range");
461 CheckContext(this);
462 alDopplerFactor(factor);
466 void ALContext::setSpeedOfSound(ALfloat speed)
468 if(!(speed > 0.0f))
469 throw std::runtime_error("Speed of sound out of range");
470 CheckContext(this);
471 alSpeedOfSound(speed);
475 void ALContext::setDistanceModel(DistanceModel model)
477 CheckContext(this);
478 alDistanceModel(model);
482 void ALContext::update()
484 CheckContext(this);
485 std::for_each(mUsedSources.begin(), mUsedSources.end(), std::mem_fun(&ALSource::updateNoCtxCheck));
489 void ALContext::setGain(ALfloat gain)
491 if(!(gain >= 0.0f))
492 throw std::runtime_error("Gain out of range");
493 CheckContext(this);
494 alListenerf(AL_GAIN, gain);
498 void ALContext::setPosition(ALfloat x, ALfloat y, ALfloat z)
500 CheckContext(this);
501 alListener3f(AL_POSITION, x, y, z);
504 void ALContext::setPosition(const ALfloat *pos)
506 CheckContext(this);
507 alListenerfv(AL_POSITION, pos);
510 void ALContext::setVelocity(ALfloat x, ALfloat y, ALfloat z)
512 CheckContext(this);
513 alListener3f(AL_VELOCITY, x, y, z);
516 void ALContext::setVelocity(const ALfloat *vel)
518 CheckContext(this);
519 alListenerfv(AL_VELOCITY, vel);
522 void ALContext::setOrientation(ALfloat x1, ALfloat y1, ALfloat z1, ALfloat x2, ALfloat y2, ALfloat z2)
524 CheckContext(this);
525 ALfloat ori[6] = { x1, y1, z1, x2, y2, z2 };
526 alListenerfv(AL_ORIENTATION, ori);
529 void ALContext::setOrientation(const ALfloat *at, const ALfloat *up)
531 CheckContext(this);
532 ALfloat ori[6] = { at[0], at[1], at[2], up[0], up[1], up[2] };
533 alListenerfv(AL_ORIENTATION, ori);
536 void ALContext::setOrientation(const ALfloat *ori)
538 CheckContext(this);
539 alListenerfv(AL_ORIENTATION, ori);
543 void Context::MakeCurrent(Context *context)
545 ALContext *ctx = 0;
546 if(context)
548 ctx = cast<ALContext*>(context);
549 if(!ctx) throw std::runtime_error("Invalid context pointer");
551 ALContext::MakeCurrent(ctx);
554 Context *Context::GetCurrent()
556 return ALContext::GetCurrent();
559 void Context::MakeThreadCurrent(Context *context)
561 ALContext *ctx = 0;
562 if(context)
564 ctx = cast<ALContext*>(context);
565 if(!ctx) throw std::runtime_error("Invalid context pointer");
567 ALContext::MakeThreadCurrent(ctx);
570 Context *Context::GetThreadCurrent()
572 return ALContext::GetThreadCurrent();