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