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> ¶ms)
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> ¶ms)
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