1 /**
2  * Copyright (c) 2006-2019 LOVE Development Team
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  **/
20 
21 #include "Audio.h"
22 #include "common/delay.h"
23 #include "RecordingDevice.h"
24 #include "sound/Decoder.h"
25 
26 #include <cstdlib>
27 #include <iostream>
28 
29 namespace love
30 {
31 namespace audio
32 {
33 namespace openal
34 {
35 
PoolThread(Pool * pool)36 Audio::PoolThread::PoolThread(Pool *pool)
37 	: pool(pool)
38 	, finish(false)
39 {
40 	threadName = "AudioPool";
41 }
42 
~PoolThread()43 Audio::PoolThread::~PoolThread()
44 {
45 }
46 
47 
threadFunction()48 void Audio::PoolThread::threadFunction()
49 {
50 	while (true)
51 	{
52 		{
53 			thread::Lock lock(mutex);
54 			if (finish)
55 			{
56 				return;
57 			}
58 		}
59 
60 		pool->update();
61 		sleep(5);
62 	}
63 }
64 
setFinish()65 void Audio::PoolThread::setFinish()
66 {
67 	thread::Lock lock(mutex);
68 	finish = true;
69 }
70 
getFormat(int bitDepth,int channels)71 ALenum Audio::getFormat(int bitDepth, int channels)
72 {
73 	if (bitDepth != 8 && bitDepth != 16)
74 		return AL_NONE;
75 
76 	if (channels == 1)
77 		return bitDepth == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
78 	else if (channels == 2)
79 		return bitDepth == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
80 #ifdef AL_EXT_MCFORMATS
81 	else if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
82 	{
83 		if (channels == 6)
84 			return bitDepth == 8 ? AL_FORMAT_51CHN8 : AL_FORMAT_51CHN16;
85 		else if (channels == 8)
86 			return bitDepth == 8 ? AL_FORMAT_71CHN8 : AL_FORMAT_71CHN16;
87 	}
88 #endif
89 	return AL_NONE;
90 }
91 
Audio()92 Audio::Audio()
93 	: device(nullptr)
94 	, context(nullptr)
95 	, pool(nullptr)
96 	, poolThread(nullptr)
97 	, distanceModel(DISTANCE_INVERSE_CLAMPED)
98 {
99 #if defined(LOVE_LINUX)
100 	// Temporarly block signals, as the thread inherits this mask
101 	love::thread::disableSignals();
102 #endif
103 
104 	// Before opening new device, check if recording
105 	// is requested.
106 	if (getRequestRecordingPermission())
107 	{
108 		if (!hasRecordingPermission())
109 			// Request recording permission on some OSes.
110 			requestRecordingPermission();
111 	}
112 
113 	// Passing null for default device.
114 	device = alcOpenDevice(nullptr);
115 
116 	if (device == nullptr)
117 		throw love::Exception("Could not open device.");
118 
119 #ifdef ALC_EXT_EFX
120 	ALint attribs[4] = { ALC_MAX_AUXILIARY_SENDS, MAX_SOURCE_EFFECTS, 0, 0 };
121 #else
122 	ALint *attribs = nullptr;
123 #endif
124 
125 	context = alcCreateContext(device, attribs);
126 
127 	if (context == nullptr)
128 		throw love::Exception("Could not create context.");
129 
130 	if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
131 		throw love::Exception("Could not make context current.");
132 
133 #if defined(LOVE_LINUX)
134 	love::thread::reenableSignals();
135 #endif
136 
137 #ifdef ALC_EXT_EFX
138 	initializeEFX();
139 
140 	alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &MAX_SOURCE_EFFECTS);
141 
142 	alGetError();
143 	if (alGenAuxiliaryEffectSlots)
144 	{
145 		for (int i = 0; i < MAX_SCENE_EFFECTS; i++)
146 		{
147 			ALuint slot;
148 			alGenAuxiliaryEffectSlots(1, &slot);
149 			if (alGetError() == AL_NO_ERROR)
150 				slotlist.push(slot);
151 			else
152 			{
153 				MAX_SCENE_EFFECTS = i;
154 				break;
155 			}
156 		}
157 	}
158 	else
159 		MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
160 #else
161 	MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
162 #endif
163 
164 	try
165 	{
166 		pool = new Pool();
167 	}
168 	catch (love::Exception &)
169 	{
170 		for (auto c : capture)
171 			delete c;
172 
173 #ifdef ALC_EXT_EFX
174 		if (alDeleteAuxiliaryEffectSlots)
175 		{
176 			while (!slotlist.empty())
177 			{
178 				alDeleteAuxiliaryEffectSlots(1, &slotlist.top());
179 				slotlist.pop();
180 			}
181 		}
182 #endif
183 
184 		alcMakeContextCurrent(nullptr);
185 		alcDestroyContext(context);
186 		alcCloseDevice(device);
187 		throw;
188 	}
189 
190 	poolThread = new PoolThread(pool);
191 	poolThread->start();
192 }
193 
~Audio()194 Audio::~Audio()
195 {
196 	poolThread->setFinish();
197 	poolThread->wait();
198 
199 	delete poolThread;
200 	delete pool;
201 
202 	for (auto c : capture)
203 		delete c;
204 
205 #ifdef ALC_EXT_EFX
206 	for (auto e : effectmap)
207 	{
208 		delete e.second.effect;
209 		slotlist.push(e.second.slot);
210 	}
211 
212 	if (alDeleteAuxiliaryEffectSlots)
213 	{
214 		while (!slotlist.empty())
215 		{
216 			alDeleteAuxiliaryEffectSlots(1, &slotlist.top());
217 			slotlist.pop();
218 		}
219 	}
220 #endif
221 	alcMakeContextCurrent(nullptr);
222 	alcDestroyContext(context);
223 	alcCloseDevice(device);
224 }
225 
getName() const226 const char *Audio::getName() const
227 {
228 	return "love.audio.openal";
229 }
230 
newSource(love::sound::Decoder * decoder)231 love::audio::Source *Audio::newSource(love::sound::Decoder *decoder)
232 {
233 	return new Source(pool, decoder);
234 }
235 
newSource(love::sound::SoundData * soundData)236 love::audio::Source *Audio::newSource(love::sound::SoundData *soundData)
237 {
238 	return new Source(pool, soundData);
239 }
240 
newSource(int sampleRate,int bitDepth,int channels,int buffers)241 love::audio::Source *Audio::newSource(int sampleRate, int bitDepth, int channels, int buffers)
242 {
243 	return new Source(pool, sampleRate, bitDepth, channels, buffers);
244 }
245 
getActiveSourceCount() const246 int Audio::getActiveSourceCount() const
247 {
248 	return pool->getActiveSourceCount();
249 }
250 
getMaxSources() const251 int Audio::getMaxSources() const
252 {
253 	return pool->getMaxSources();
254 }
255 
play(love::audio::Source * source)256 bool Audio::play(love::audio::Source *source)
257 {
258 	return source->play();
259 }
260 
play(const std::vector<love::audio::Source * > & sources)261 bool Audio::play(const std::vector<love::audio::Source*> &sources)
262 {
263 	return Source::play(sources);
264 }
265 
stop(love::audio::Source * source)266 void Audio::stop(love::audio::Source *source)
267 {
268 	source->stop();
269 }
270 
stop(const std::vector<love::audio::Source * > & sources)271 void Audio::stop(const std::vector<love::audio::Source*> &sources)
272 {
273 	return Source::stop(sources);
274 }
275 
stop()276 void Audio::stop()
277 {
278 	return Source::stop(pool);
279 }
280 
pause(love::audio::Source * source)281 void Audio::pause(love::audio::Source *source)
282 {
283 	source->pause();
284 }
285 
pause(const std::vector<love::audio::Source * > & sources)286 void Audio::pause(const std::vector<love::audio::Source*> &sources)
287 {
288 	return Source::pause(sources);
289 }
290 
pause()291 std::vector<love::audio::Source*> Audio::pause()
292 {
293 	return Source::pause(pool);
294 }
295 
setVolume(float volume)296 void Audio::setVolume(float volume)
297 {
298 	alListenerf(AL_GAIN, volume);
299 }
300 
getVolume() const301 float Audio::getVolume() const
302 {
303 	ALfloat volume;
304 	alGetListenerf(AL_GAIN, &volume);
305 	return volume;
306 }
307 
getPosition(float * v) const308 void Audio::getPosition(float *v) const
309 {
310 	alGetListenerfv(AL_POSITION, v);
311 }
312 
setPosition(float * v)313 void Audio::setPosition(float *v)
314 {
315 	alListenerfv(AL_POSITION, v);
316 }
317 
getOrientation(float * v) const318 void Audio::getOrientation(float *v) const
319 {
320 	alGetListenerfv(AL_ORIENTATION, v);
321 }
322 
setOrientation(float * v)323 void Audio::setOrientation(float *v)
324 {
325 	alListenerfv(AL_ORIENTATION, v);
326 }
327 
getVelocity(float * v) const328 void Audio::getVelocity(float *v) const
329 {
330 	alGetListenerfv(AL_VELOCITY, v);
331 }
332 
setVelocity(float * v)333 void Audio::setVelocity(float *v)
334 {
335 	alListenerfv(AL_VELOCITY, v);
336 }
337 
setDopplerScale(float scale)338 void Audio::setDopplerScale(float scale)
339 {
340 	if (scale >= 0.0f)
341 		alDopplerFactor(scale);
342 }
343 
getDopplerScale() const344 float Audio::getDopplerScale() const
345 {
346 	return alGetFloat(AL_DOPPLER_FACTOR);
347 }
348 /*
349 void Audio::setMeter(float scale)
350 {
351 	if (scale >= 0.0f)
352 	{
353 		metersPerUnit = scale;
354 #ifdef ALC_EXT_EFX
355 		alListenerf(AL_METERS_PER_UNIT, scale);
356 #endif
357 	}
358 }
359 
360 float Audio::getMeter() const
361 {
362 	return metersPerUnit;
363 }
364 */
getDistanceModel() const365 Audio::DistanceModel Audio::getDistanceModel() const
366 {
367 	return distanceModel;
368 }
369 
setDistanceModel(DistanceModel distanceModel)370 void Audio::setDistanceModel(DistanceModel distanceModel)
371 {
372 	this->distanceModel = distanceModel;
373 
374 	switch (distanceModel)
375 	{
376 	case DISTANCE_NONE:
377 		alDistanceModel(AL_NONE);
378 		break;
379 
380 	case DISTANCE_INVERSE:
381 		alDistanceModel(AL_INVERSE_DISTANCE);
382 		break;
383 
384 	case DISTANCE_INVERSE_CLAMPED:
385 		alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
386 		break;
387 
388 	case DISTANCE_LINEAR:
389 		alDistanceModel(AL_LINEAR_DISTANCE);
390 		break;
391 
392 	case DISTANCE_LINEAR_CLAMPED:
393 		alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
394 		break;
395 
396 	case DISTANCE_EXPONENT:
397 		alDistanceModel(AL_EXPONENT_DISTANCE);
398 		break;
399 
400 	case DISTANCE_EXPONENT_CLAMPED:
401 		alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
402 		break;
403 
404 	default:
405 		break;
406 	}
407 }
408 
getRecordingDevices()409 const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
410 {
411 	std::vector<std::string> devnames;
412 	std::vector<love::audio::RecordingDevice*> devices;
413 
414 	// If recording permission is not granted, inform user about it
415 	// and return empty list.
416 	if (!hasRecordingPermission() && getRequestRecordingPermission())
417 	{
418 		showRecordingPermissionMissingDialog();
419 		capture.clear();
420 		return capture;
421 	}
422 
423 	std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
424 
425 	//no device name obtained from AL, fallback to reading from device
426 	if (defaultname.length() == 0)
427 	{
428 		//use some safe basic parameters - 8 kHz, 8 bits, 1 channel
429 		ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO8, 1024);
430 		if (alGetError() == AL_NO_ERROR)
431 		{
432 			defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER);
433 			alcCaptureCloseDevice(defaultdevice);
434 		}
435 		else
436 		{
437 			//failed to open default recording device - bail, return empty list
438 			capture.clear();
439 			return capture;
440 		}
441 	}
442 
443 	devnames.reserve(capture.size());
444 	devnames.push_back(defaultname);
445 
446 	//find devices name list
447 	const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
448 	size_t offset = 0;
449 	while (true)
450 	{
451 		if (devstr[offset] == '\0')
452 			break;
453 		std::string str((ALCchar*)&devstr[offset]);
454 		if (str != defaultname)
455 			devnames.push_back(str);
456 		offset += str.length() + 1;
457 	}
458 
459 	devices.reserve(devnames.size());
460 	//build ordered list of devices
461 	for (int i = 0; i < (int) devnames.size(); i++)
462 	{
463 		devices.push_back(nullptr);
464 		auto d = devices.end() - 1;
465 
466 		for (auto c : capture)
467 			if (devnames[i] == c->getName())
468 				*d = c;
469 
470 		if (*d == nullptr)
471 			*d = new RecordingDevice(devnames[i].c_str());
472 		else
473 			(*d)->retain();
474 	}
475 
476 	for (auto c : capture)
477 		c->release();
478 	capture.clear();
479 	capture.reserve(devices.size());
480 
481 	//this needs to be executed in specific order
482 	for (unsigned int i = 0; i < devnames.size(); i++)
483 		capture.push_back(devices[i]);
484 
485 	return capture;
486 }
487 
setEffect(const char * name,std::map<Effect::Parameter,float> & params)488 bool Audio::setEffect(const char *name, std::map<Effect::Parameter, float> &params)
489 {
490 	Effect *effect;
491 	ALuint slot;
492 
493 	auto iter = effectmap.find(name);
494 	if (iter == effectmap.end())
495 	{
496 		//new effect needed but no more slots
497 		if (effectmap.size() >= (unsigned int)MAX_SCENE_EFFECTS)
498 			return false;
499 
500 		effect = new Effect();
501 		slot = slotlist.top();
502 		slotlist.pop();
503 
504 		effectmap[name] = {effect, slot};
505 	}
506 	else
507 	{
508 		effect = iter->second.effect;
509 		slot = iter->second.slot;
510 	}
511 
512 	bool result = effect->setParams(params);
513 
514 #ifdef ALC_EXT_EFX
515 	if (alAuxiliaryEffectSloti)
516 	{
517 		if (result)
518 		{
519 			auto iter = params.find(Effect::EFFECT_VOLUME);
520 			if (iter != params.end())
521 				alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, iter->second);
522 			alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect->getEffect());
523 		}
524 		else
525 			alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
526 		alGetError();
527 	}
528 #endif
529 
530 	return result;
531 }
532 
unsetEffect(const char * name)533 bool Audio::unsetEffect(const char *name)
534 {
535 	auto iter = effectmap.find(name);
536 	if (iter == effectmap.end())
537 		return false;
538 
539 	Effect *effect = iter->second.effect;
540 	ALuint slot = iter->second.slot;
541 
542 #ifdef ALC_EXT_EFX
543 	if (alAuxiliaryEffectSloti)
544 		alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
545 #endif
546 
547 	delete effect;
548 	effectmap.erase(iter);
549 	slotlist.push(slot);
550 	return true;
551 }
552 
getEffect(const char * name,std::map<Effect::Parameter,float> & params)553 bool Audio::getEffect(const char *name, std::map<Effect::Parameter, float> &params)
554 {
555 	auto iter = effectmap.find(name);
556 	if (iter == effectmap.end())
557 		return false;
558 
559 	params = iter->second.effect->getParams();
560 
561 	return true;
562 }
563 
getActiveEffects(std::vector<std::string> & list) const564 bool Audio::getActiveEffects(std::vector<std::string> &list) const
565 {
566 	if (effectmap.empty())
567 		return false;
568 
569 	list.reserve(effectmap.size());
570 	for (auto i : effectmap)
571 		list.push_back(i.first);
572 
573 	return true;
574 }
575 
getMaxSceneEffects() const576 int Audio::getMaxSceneEffects() const
577 {
578 	return MAX_SCENE_EFFECTS;
579 }
580 
getMaxSourceEffects() const581 int Audio::getMaxSourceEffects() const
582 {
583 	return MAX_SOURCE_EFFECTS;
584 }
585 
isEFXsupported() const586 bool Audio::isEFXsupported() const
587 {
588 #ifdef ALC_EXT_EFX
589 	return (alGenEffects != nullptr);
590 #else
591 	return false;
592 #endif
593 }
594 
getEffectID(const char * name,ALuint & id)595 bool Audio::getEffectID(const char *name, ALuint &id)
596 {
597 	auto iter = effectmap.find(name);
598 	if (iter == effectmap.end())
599 		return false;
600 
601 	id = iter->second.slot;
602 	return true;
603 }
604 
605 #ifdef ALC_EXT_EFX
606 LPALGENEFFECTS alGenEffects = nullptr;
607 LPALDELETEEFFECTS alDeleteEffects = nullptr;
608 LPALISEFFECT alIsEffect = nullptr;
609 LPALEFFECTI alEffecti = nullptr;
610 LPALEFFECTIV alEffectiv = nullptr;
611 LPALEFFECTF alEffectf = nullptr;
612 LPALEFFECTFV alEffectfv = nullptr;
613 LPALGETEFFECTI alGetEffecti = nullptr;
614 LPALGETEFFECTIV alGetEffectiv = nullptr;
615 LPALGETEFFECTF alGetEffectf = nullptr;
616 LPALGETEFFECTFV alGetEffectfv = nullptr;
617 LPALGENFILTERS alGenFilters = nullptr;
618 LPALDELETEFILTERS alDeleteFilters = nullptr;
619 LPALISFILTER alIsFilter = nullptr;
620 LPALFILTERI alFilteri = nullptr;
621 LPALFILTERIV alFilteriv = nullptr;
622 LPALFILTERF alFilterf = nullptr;
623 LPALFILTERFV alFilterfv = nullptr;
624 LPALGETFILTERI alGetFilteri = nullptr;
625 LPALGETFILTERIV alGetFilteriv = nullptr;
626 LPALGETFILTERF alGetFilterf = nullptr;
627 LPALGETFILTERFV alGetFilterfv = nullptr;
628 LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots = nullptr;
629 LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots = nullptr;
630 LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot = nullptr;
631 LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti = nullptr;
632 LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv = nullptr;
633 LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf = nullptr;
634 LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv = nullptr;
635 LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti = nullptr;
636 LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv = nullptr;
637 LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf = nullptr;
638 LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv = nullptr;
639 #endif
640 
initializeEFX()641 void Audio::initializeEFX()
642 {
643 #ifdef ALC_EXT_EFX
644 	if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_FALSE)
645 		return;
646 
647 	alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
648 	alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
649 	alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
650 	alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
651 	alEffectiv = (LPALEFFECTIV)alGetProcAddress("alEffectiv");
652 	alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
653 	alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
654 	alGetEffecti = (LPALGETEFFECTI)alGetProcAddress("alGetEffecti");
655 	alGetEffectiv = (LPALGETEFFECTIV)alGetProcAddress("alGetEffectiv");
656 	alGetEffectf = (LPALGETEFFECTF)alGetProcAddress("alGetEffectf");
657 	alGetEffectfv = (LPALGETEFFECTFV)alGetProcAddress("alGetEffectfv");
658 	alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
659 	alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
660 	alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
661 	alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
662 	alFilteriv = (LPALFILTERIV)alGetProcAddress("alFilteriv");
663 	alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
664 	alFilterfv = (LPALFILTERFV)alGetProcAddress("alFilterfv");
665 	alGetFilteri = (LPALGETFILTERI)alGetProcAddress("alGetFilteri");
666 	alGetFilteriv = (LPALGETFILTERIV)alGetProcAddress("alGetFilteriv");
667 	alGetFilterf = (LPALGETFILTERF)alGetProcAddress("alGetFilterf");
668 	alGetFilterfv = (LPALGETFILTERFV)alGetProcAddress("alGetFilterfv");
669 	alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
670 	alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
671 	alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");
672 	alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
673 	alAuxiliaryEffectSlotiv = (LPALAUXILIARYEFFECTSLOTIV)alGetProcAddress("alAuxiliaryEffectSlotiv");
674 	alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
675 	alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv");
676 	alGetAuxiliaryEffectSloti = (LPALGETAUXILIARYEFFECTSLOTI)alGetProcAddress("alGetAuxiliaryEffectSloti");
677 	alGetAuxiliaryEffectSlotiv = (LPALGETAUXILIARYEFFECTSLOTIV)alGetProcAddress("alGetAuxiliaryEffectSlotiv");
678 	alGetAuxiliaryEffectSlotf = (LPALGETAUXILIARYEFFECTSLOTF)alGetProcAddress("alGetAuxiliaryEffectSlotf");
679 	alGetAuxiliaryEffectSlotfv = (LPALGETAUXILIARYEFFECTSLOTFV)alGetProcAddress("alGetAuxiliaryEffectSlotfv");
680 
681 	//failed to initialize functions, revert to nullptr
682 	if (!alGenEffects || !alDeleteEffects || !alIsEffect ||
683 		!alGenFilters || !alDeleteFilters || !alIsFilter ||
684 		!alGenAuxiliaryEffectSlots || !alDeleteAuxiliaryEffectSlots || !alIsAuxiliaryEffectSlot ||
685 		!alEffecti || !alEffectiv || !alEffectf || !alEffectfv ||
686 		!alGetEffecti || !alGetEffectiv || !alGetEffectf || !alGetEffectfv ||
687 		!alFilteri || !alFilteriv || !alFilterf || !alFilterfv ||
688 		!alGetFilteri || !alGetFilteriv || !alGetFilterf || !alGetFilterfv ||
689 		!alAuxiliaryEffectSloti || !alAuxiliaryEffectSlotiv || !alAuxiliaryEffectSlotf || !alAuxiliaryEffectSlotfv ||
690 		!alGetAuxiliaryEffectSloti || !alGetAuxiliaryEffectSlotiv || !alGetAuxiliaryEffectSlotf || !alGetAuxiliaryEffectSlotfv)
691 	{
692 		alGenEffects = nullptr; alDeleteEffects = nullptr; alIsEffect = nullptr;
693 		alEffecti = nullptr; alEffectiv = nullptr; alEffectf = nullptr; alEffectfv = nullptr;
694 		alGetEffecti = nullptr; alGetEffectiv = nullptr; alGetEffectf = nullptr; alGetEffectfv = nullptr;
695 		alGenFilters = nullptr; alDeleteFilters = nullptr; alIsFilter = nullptr;
696 		alFilteri = nullptr; alFilteriv = nullptr; alFilterf = nullptr; alFilterfv = nullptr;
697 		alGetFilteri = nullptr; alGetFilteriv = nullptr; alGetFilterf = nullptr; alGetFilterfv = nullptr;
698 		alGenAuxiliaryEffectSlots = nullptr; alDeleteAuxiliaryEffectSlots = nullptr; alIsAuxiliaryEffectSlot = nullptr;
699 		alAuxiliaryEffectSloti = nullptr; alAuxiliaryEffectSlotiv = nullptr;
700 		alAuxiliaryEffectSlotf = nullptr; alAuxiliaryEffectSlotfv = nullptr;
701 		alGetAuxiliaryEffectSloti = nullptr; alGetAuxiliaryEffectSlotiv = nullptr;
702 		alGetAuxiliaryEffectSlotf = nullptr; alGetAuxiliaryEffectSlotfv = nullptr;
703 	}
704 
705 #endif
706 }
707 
708 } // openal
709 } // audio
710 } // love
711