1 /*
2  Copyright (c) 2013 yvt
3 
4  This file is part of OpenSpades.
5 
6  OpenSpades is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OpenSpades is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
18 
19  */
20 
21 #include <cstring>
22 #include <exception>
23 #include <cstdlib>
24 #include <vector>
25 #include <utility>
26 
27 #include "ALDevice.h"
28 #include "ALFuncs.h"
29 #include <Client/GameMap.h>
30 #include <Client/IAudioChunk.h>
31 #include <Core/Debug.h>
32 #include <Core/Exception.h>
33 #include <Core/FileManager.h>
34 #include <Core/IAudioStream.h>
35 #include <Core/Settings.h>
36 #include <Core/AudioStream.h>
37 
38 DEFINE_SPADES_SETTING(s_maxPolyphonics, "96");
39 DEFINE_SPADES_SETTING(s_eax, "1");
40 DEFINE_SPADES_SETTING(s_alPreciseErrorCheck, "1");
41 DEFINE_SPADES_SETTING(s_gain, "1");
42 
43 // lm: seems to be missing for me..
44 #ifndef ALC_ALL_DEVICES_SPECIFIER
45 #define ALC_ALL_DEVICES_SPECIFIER 0x1013
46 #endif
47 
48 #define ALCheckErrorPrecise()                                                                      \
49 	if (s_alPreciseErrorCheck)                                                                     \
50 	ALCheckError()
51 
52 namespace spades {
53 	namespace audio {
54 
TransformVectorToAL(Vector3 v)55 		static Vector3 TransformVectorToAL(Vector3 v) { return MakeVector3(v.x, v.y, v.z); }
TransformVectorFromAL(Vector3 v)56 		static Vector3 TransformVectorFromAL(Vector3 v) { return MakeVector3(v.x, v.y, v.z); }
57 
58 		namespace {
ConvertFloatBufferToSignedShort(const std::vector<uint8_t> & bytes)59 			std::vector<uint8_t> ConvertFloatBufferToSignedShort(const std::vector<uint8_t> &bytes) {
60 				if (bytes.size() & 3) {
61 					SPRaise("Size is invalid");
62 				}
63 				std::vector<uint8_t> ret;
64 				ret.resize(bytes.size() >> 1);
65 
66 				for (size_t i = 0; i < bytes.size(); i += 4) {
67 					float inValue = reinterpret_cast<const float *>(bytes.data())[i >> 2];
68 					int16_t &outValue = reinterpret_cast<std::int16_t *>(ret.data())[i >> 2];
69 					outValue = static_cast<int16_t>(std::floor(inValue * 32768.f));
70 				}
71 
72 				return ret;
73 			}
74 		}
75 
76 		class ALAudioChunk : public client::IAudioChunk {
77 			ALuint handle;
78 			ALuint format;
79 
80 		protected:
~ALAudioChunk()81 			virtual ~ALAudioChunk() {
82 				SPADES_MARK_FUNCTION();
83 
84 				al::qalDeleteBuffers(1, &handle);
85 				ALCheckErrorPrecise();
86 			}
87 
88 		public:
GetHandle()89 			ALuint GetHandle() { return handle; }
ALAudioChunk(IAudioStream * audioStream)90 			ALAudioChunk(IAudioStream *audioStream) {
91 				SPADES_MARK_FUNCTION();
92 
93 				std::vector<uint8_t> bytes;
94 				if (audioStream->GetLength() > 128 * 1024 * 1024) {
95 					SPRaise("Audio stream too long");
96 				}
97 
98 				size_t len = (size_t)audioStream->GetLength();
99 				bytes.resize(len);
100 
101 				audioStream->SetPosition(0);
102 				if (audioStream->Read(bytes.data(), len) < len)
103 					SPRaise("Failed to read audio data");
104 
105 				ALuint alFormat;
106 				switch (audioStream->GetSampleFormat()) {
107 					case IAudioStream::UnsignedByte:
108 						switch (audioStream->GetNumChannels()) {
109 							case 1: alFormat = AL_FORMAT_MONO8; break;
110 							case 2: alFormat = AL_FORMAT_STEREO8; break;
111 							default: SPRaise("Unsupported audio format");
112 						}
113 						break;
114 					case IAudioStream::SignedShort:
115 						switch (audioStream->GetNumChannels()) {
116 							case 1: alFormat = AL_FORMAT_MONO16; break;
117 							case 2: alFormat = AL_FORMAT_STEREO16; break;
118 							default: SPRaise("Unsupported audio format");
119 						}
120 						break;
121 					case IAudioStream::SingleFloat:
122 						bytes = ConvertFloatBufferToSignedShort(bytes);
123 						switch (audioStream->GetNumChannels()) {
124 							case 1: alFormat = AL_FORMAT_MONO16; break;
125 							case 2: alFormat = AL_FORMAT_STEREO16; break;
126 							default: SPRaise("Unsupported audio format");
127 						}
128 						break;
129 					default: SPRaise("Unsupported audio format");
130 				}
131 
132 				format = alFormat;
133 
134 				al::qalGenBuffers(1, &handle);
135 				ALCheckError();
136 				al::qalBufferData(handle, alFormat, bytes.data(), (ALuint)bytes.size(),
137 				                  audioStream->GetSamplingFrequency());
138 				ALCheckError();
139 			}
140 
GetFormat()141 			ALuint GetFormat() { return format; }
142 		};
143 
144 		class ALDevice::Internal {
145 		public:
146 			bool useEAX;
147 			ALCdevice *alDevice;
148 			ALCcontext *alContext;
149 
150 			ALuint reverbFXSlot;
151 			ALuint reverbFX;
152 
153 			ALuint obstructionFilter;
154 
155 			client::GameMap *map;
156 
157 			struct ALSrc {
158 				Internal *internal;
159 				ALuint handle;
160 				bool eaxSource;
161 				bool stereo;
162 				bool local;
163 				client::AudioParam param;
164 
ALSrcspades::audio::ALDevice::Internal::ALSrc165 				ALSrc(Internal *i) : internal(i) {
166 					SPADES_MARK_FUNCTION();
167 
168 					al::qalGenSources(1, &handle);
169 					ALCheckError();
170 				}
171 
~ALSrcspades::audio::ALDevice::Internal::ALSrc172 				~ALSrc() {
173 					SPADES_MARK_FUNCTION();
174 
175 					al::qalDeleteSources(1, &handle);
176 					ALCheckErrorPrecise();
177 				}
178 
Terminatespades::audio::ALDevice::Internal::ALSrc179 				void Terminate() {
180 					SPADES_MARK_FUNCTION();
181 
182 					al::qalSourceStop(handle);
183 					ALCheckErrorPrecise();
184 					al::qalSourcei(handle, AL_BUFFER, 0);
185 					ALCheckError();
186 				}
187 
IsPlayingspades::audio::ALDevice::Internal::ALSrc188 				bool IsPlaying() {
189 					SPADES_MARK_FUNCTION();
190 
191 					ALint value;
192 					al::qalGetSourcei(handle, AL_BUFFER, &value);
193 					ALCheckError();
194 					if (value == 0)
195 						return false;
196 
197 					al::qalGetSourcei(handle, AL_SOURCE_STATE, &value);
198 					ALCheckError();
199 					if (value == AL_STOPPED)
200 						return false;
201 					return true;
202 				}
203 
204 				// must be called
SetParamspades::audio::ALDevice::Internal::ALSrc205 				void SetParam(const client::AudioParam &param) {
206 					SPADES_MARK_FUNCTION();
207 
208 					al::qalSourcef(handle, AL_PITCH, param.pitch);
209 					ALCheckErrorPrecise();
210 					al::qalSourcef(handle, AL_GAIN, param.volume * std::max<float>(std::min<float>(s_gain, 4.0f), 0.0f));
211 					ALCheckErrorPrecise();
212 					al::qalSourcef(handle, AL_REFERENCE_DISTANCE, param.referenceDistance);
213 
214 					ALCheckError();
215 					this->param = param;
216 				}
217 
218 				// either Set3D or Set2D must be called
Set3Dspades::audio::ALDevice::Internal::ALSrc219 				void Set3D(const Vector3 &v, bool local = false) {
220 					SPADES_MARK_FUNCTION();
221 
222 					ALfloat pos[] = {v.x, v.y, v.z};
223 					ALfloat vel[] = {0, 0, 0};
224 					al::qalSourcefv(handle, AL_POSITION, pos);
225 					ALCheckErrorPrecise();
226 					al::qalSourcefv(handle, AL_VELOCITY, vel);
227 					ALCheckErrorPrecise();
228 					al::qalSourcei(handle, AL_SOURCE_RELATIVE, local ? AL_TRUE : AL_FALSE);
229 					ALCheckErrorPrecise();
230 					al::qalSourcef(handle, AL_ROLLOFF_FACTOR, (local || stereo) ? 0.f : 1.f);
231 					ALCheckErrorPrecise();
232 					if (internal->useEAX) {
233 						al::qalSource3i(handle, AL_AUXILIARY_SEND_FILTER, internal->reverbFXSlot, 0,
234 						                AL_FILTER_NULL);
235 						ALCheckErrorPrecise();
236 					}
237 					eaxSource = true;
238 					this->local = local;
239 					ALCheckError();
240 				}
241 
Set2Dspades::audio::ALDevice::Internal::ALSrc242 				void Set2D() {
243 					SPADES_MARK_FUNCTION();
244 
245 					ALfloat pos[] = {0, 0, 0};
246 					ALfloat vel[] = {0, 0, 0};
247 					al::qalSourcefv(handle, AL_POSITION, pos);
248 					ALCheckErrorPrecise();
249 					al::qalSourcefv(handle, AL_VELOCITY, vel);
250 					ALCheckErrorPrecise();
251 					al::qalSourcei(handle, AL_SOURCE_RELATIVE, AL_TRUE);
252 					ALCheckErrorPrecise();
253 					al::qalSourcef(handle, AL_ROLLOFF_FACTOR, 0.f);
254 					ALCheckErrorPrecise();
255 					if (internal->useEAX) {
256 						al::qalSource3i(handle, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0,
257 						                AL_FILTER_NULL);
258 						ALCheckErrorPrecise();
259 						al::qalSourcei(handle, AL_DIRECT_FILTER, 0);
260 						ALCheckErrorPrecise();
261 					}
262 					ALCheckError();
263 					eaxSource = false;
264 					local = true;
265 				}
266 
267 				// after calling Set2D/Set3D, must be called
UpdateObstructionspades::audio::ALDevice::Internal::ALSrc268 				void UpdateObstruction() {
269 					SPADES_MARK_FUNCTION();
270 
271 					// update stereo source's volume (not spatialized by AL)
272 					// FIXME: move to another function?
273 					if (stereo && !local) {
274 						ALfloat v3[3];
275 						al::qalGetListenerfv(AL_POSITION, v3);
276 						Vector3 eye = {v3[0], v3[1], v3[2]};
277 						ALCheckErrorPrecise();
278 						al::qalGetSourcefv(handle, AL_POSITION, v3);
279 						Vector3 pos = {v3[0], v3[1], v3[2]};
280 						ALCheckErrorPrecise();
281 
282 						float dist = (pos - eye).GetLength();
283 						dist /= param.referenceDistance;
284 						if (dist < 1.f)
285 							dist = 1.f;
286 						dist = 1.f / dist;
287 						al::qalSourcef(handle, AL_GAIN, param.volume * dist);
288 						ALCheckError();
289 					}
290 
291 					if (!internal->useEAX)
292 						return;
293 
294 					ALint value;
295 
296 					al::qalGetSourcei(handle, AL_SOURCE_RELATIVE, &value);
297 					ALCheckErrorPrecise();
298 
299 					bool enableObstruction = true;
300 					if (value)
301 						enableObstruction = false;
302 
303 					// raytrace
304 					client::GameMap *map = internal->map;
305 					if (map && enableObstruction) {
306 						ALfloat v3[3];
307 						al::qalGetListenerfv(AL_POSITION, v3);
308 						Vector3 eye = {v3[0], v3[1], v3[2]};
309 						ALCheckErrorPrecise();
310 						al::qalGetSourcefv(handle, AL_POSITION, v3);
311 						Vector3 pos = {v3[0], v3[1], v3[2]};
312 						ALCheckErrorPrecise();
313 						Vector3 checkPos;
314 						eye = TransformVectorFromAL(eye);
315 						pos = TransformVectorFromAL(pos);
316 						for (int x = -1; x <= 1; x++)
317 							for (int y = -1; y <= 1; y++)
318 								for (int z = -1; z <= 1; z++) {
319 									IntVector3 hitPos;
320 									checkPos.x = pos.x + (float)x * .2f;
321 									checkPos.y = pos.y + (float)y * .2f;
322 									checkPos.z = pos.z + (float)z * .2f;
323 									if (!map->CastRay(eye, (checkPos - eye).Normalize(),
324 									                  (checkPos - eye).GetLength(), hitPos)) {
325 										enableObstruction = false;
326 									}
327 								}
328 					} else {
329 						enableObstruction = false;
330 					}
331 
332 					ALuint fx = AL_EFFECTSLOT_NULL;
333 					ALuint flt = AL_FILTER_NULL;
334 
335 					if (enableObstruction)
336 						flt = internal->obstructionFilter;
337 
338 					if (eaxSource)
339 						fx = internal->reverbFXSlot;
340 
341 					al::qalSource3i(handle, AL_AUXILIARY_SEND_FILTER, fx, 0, flt);
342 					ALCheckErrorPrecise();
343 					al::qalSourcei(handle, AL_DIRECT_FILTER, flt);
344 					ALCheckError();
345 				}
346 
PlayBufferOneShotspades::audio::ALDevice::Internal::ALSrc347 				void PlayBufferOneShot(ALuint buffer) {
348 					SPADES_MARK_FUNCTION();
349 
350 					al::qalSourcei(handle, AL_LOOPING, AL_FALSE);
351 					ALCheckErrorPrecise();
352 					al::qalSourcei(handle, AL_BUFFER, buffer);
353 					ALCheckErrorPrecise();
354 					al::qalSourcei(handle, AL_SAMPLE_OFFSET, 0);
355 					ALCheckErrorPrecise();
356 					al::qalSourcePlay(handle);
357 					ALCheckError();
358 				}
359 			};
360 
361 			std::vector<ALSrc *> srcs;
362 
363 			// reverb simulator
364 			int roomHistoryPos;
365 			std::vector<float> roomHistory;
366 			std::vector<float> roomFeedbackHistory;
367 
updateEFXReverb(LPEFXEAXREVERBPROPERTIES reverb)368 			void updateEFXReverb(LPEFXEAXREVERBPROPERTIES reverb) {
369 				SPADES_MARK_FUNCTION_DEBUG();
370 
371 				al::qalEffectf(reverbFX, AL_EAXREVERB_DENSITY, reverb->flDensity);
372 				ALCheckErrorPrecise();
373 				al::qalEffectf(reverbFX, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion);
374 				ALCheckErrorPrecise();
375 				al::qalEffectf(reverbFX, AL_EAXREVERB_GAIN, reverb->flGain);
376 				ALCheckErrorPrecise();
377 				al::qalEffectf(reverbFX, AL_EAXREVERB_GAINHF, reverb->flGainHF);
378 				ALCheckErrorPrecise();
379 				al::qalEffectf(reverbFX, AL_EAXREVERB_GAINLF, reverb->flGainLF);
380 				ALCheckErrorPrecise();
381 				al::qalEffectf(reverbFX, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime);
382 				ALCheckErrorPrecise();
383 				al::qalEffectf(reverbFX, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);
384 				ALCheckErrorPrecise();
385 				al::qalEffectf(reverbFX, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio);
386 				ALCheckErrorPrecise();
387 				al::qalEffectf(reverbFX, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);
388 				ALCheckErrorPrecise();
389 				al::qalEffectf(reverbFX, AL_EAXREVERB_REFLECTIONS_DELAY,
390 				               reverb->flReflectionsDelay);
391 				ALCheckErrorPrecise();
392 				al::qalEffectfv(reverbFX, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan);
393 				ALCheckErrorPrecise();
394 				al::qalEffectf(reverbFX, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);
395 				ALCheckErrorPrecise();
396 				al::qalEffectf(reverbFX, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);
397 				ALCheckErrorPrecise();
398 				al::qalEffectfv(reverbFX, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan);
399 				ALCheckErrorPrecise();
400 				al::qalEffectf(reverbFX, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime);
401 				ALCheckErrorPrecise();
402 				al::qalEffectf(reverbFX, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth);
403 				ALCheckErrorPrecise();
404 				al::qalEffectf(reverbFX, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime);
405 				ALCheckErrorPrecise();
406 				al::qalEffectf(reverbFX, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth);
407 				ALCheckErrorPrecise();
408 				al::qalEffectf(reverbFX, AL_EAXREVERB_AIR_ABSORPTION_GAINHF,
409 				               reverb->flAirAbsorptionGainHF);
410 				ALCheckErrorPrecise();
411 				al::qalEffectf(reverbFX, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference);
412 				ALCheckErrorPrecise();
413 				al::qalEffectf(reverbFX, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference);
414 				ALCheckErrorPrecise();
415 				al::qalEffectf(reverbFX, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR,
416 				               reverb->flRoomRolloffFactor);
417 				ALCheckErrorPrecise();
418 				al::qalEffecti(reverbFX, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
419 				ALCheckErrorPrecise();
420 			}
421 
Internal()422 			Internal() {
423 				SPADES_MARK_FUNCTION();
424 
425 				if (al::qalGetString(AL_EXTENSIONS)) {
426 					std::vector<std::string> strs = Split(al::qalGetString(AL_EXTENSIONS), " ");
427 					SPLog("OpenAL Extensions:");
428 					for (size_t i = 0; i < strs.size(); i++) {
429 						SPLog("  %s", strs[i].c_str());
430 					}
431 				}
432 
433 				SPLog("--- All devices ---");
434 				const ALCchar *ext = al::qalcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
435 				while (ext && *ext) {
436 					SPLog("%s", ext);
437 					ext += (std::strlen(ext) + 1);
438 				}
439 				SPLog("-------------------");
440 
441 				SPLog("--- Devices ---");
442 				ext = al::qalcGetString(NULL, ALC_DEVICE_SPECIFIER);
443 				while (ext && *ext) {
444 					SPLog("%s", ext);
445 					ext += (std::strlen(ext) + 1);
446 				}
447 				SPLog("---------------");
448 				const ALCchar *dev = al::qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
449 				if (dev && *dev) {
450 					SPLog("Default device: %s", dev);
451 				}
452 
453 				alDevice = al::qalcOpenDevice(NULL);
454 				if (!alDevice) {
455 					if ((ext = al::qalcGetString(NULL, ALC_EXTENSIONS))) {
456 						std::vector<std::string> strs = Split(ext, " ");
457 						SPLog("OpenAL ALC Extensions (NULL):");
458 						for (size_t i = 0; i < strs.size(); i++) {
459 							SPLog("  %s", strs[i].c_str());
460 						}
461 					}
462 
463 					SPRaise("Failed to open OpenAL device.");
464 				}
465 				SPLog("OpenAL Info:");
466 				SPLog("  Vendor: %s", al::qalGetString(AL_VENDOR));
467 				SPLog("  Version: %s", al::qalGetString(AL_VERSION));
468 				SPLog("  Renderer: %s", al::qalGetString(AL_RENDERER));
469 
470 				if ((ext = al::qalcGetString(alDevice, ALC_EXTENSIONS))) {
471 					std::vector<std::string> strs = Split(ext, " ");
472 					SPLog("OpenAL ALC Extensions:");
473 					for (size_t i = 0; i < strs.size(); i++) {
474 						SPLog("  %s", strs[i].c_str());
475 					}
476 				}
477 
478 				alContext = al::qalcCreateContext(alDevice, NULL);
479 				if (!alContext) {
480 					al::qalcCloseDevice(alDevice);
481 					SPRaise("Failed to create OpenAL context.");
482 				}
483 
484 				al::qalcMakeContextCurrent(alContext);
485 
486 				map = NULL;
487 				roomHistoryPos = 0;
488 
489 				if (s_eax) {
490 					try {
491 						al::InitEAX();
492 						useEAX = true;
493 						SPLog("EAX enabled");
494 					} catch (const std::exception &ex) {
495 						useEAX = false;
496 						s_eax = 0;
497 						SPLog("Failed to initialize EAX: \n%s", ex.what());
498 					}
499 				} else {
500 					SPLog("EAX is disabled by configuration (s_eax)");
501 					useEAX = false;
502 				}
503 
504 				// somehow Apple OpenAL reports an error whose source is unknown...
505 				// so we clear that now
506 				while (al::qalGetError() != AL_NO_ERROR)
507 					;
508 
509 				ALCheckError();
510 
511 				for (int i = 0; i < (int)s_maxPolyphonics; i++) {
512 					srcs.push_back(new ALSrc(this));
513 				}
514 
515 				SPLog("%d source(s) initialized", (int)s_maxPolyphonics);
516 
517 				al::qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
518 				ALCheckErrorPrecise();
519 
520 				// initialize EAX
521 				if (useEAX) {
522 					al::qalGenAuxiliaryEffectSlots(1, &reverbFXSlot);
523 					ALCheckError();
524 					al::qalGenEffects(1, &reverbFX);
525 					ALCheckError();
526 
527 					al::qalEffecti(reverbFX, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
528 					ALCheckError();
529 
530 					EFXEAXREVERBPROPERTIES prop = EFX_REVERB_PRESET_MOUNTAINS;
531 					updateEFXReverb(&prop);
532 
533 					al::qalAuxiliaryEffectSloti(reverbFXSlot, AL_EFFECTSLOT_EFFECT, reverbFX);
534 					ALCheckError();
535 
536 					al::qalGenFilters(1, &obstructionFilter);
537 					ALCheckErrorPrecise();
538 					al::qalFilteri(obstructionFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
539 					ALCheckErrorPrecise();
540 					al::qalFilterf(obstructionFilter, AL_LOWPASS_GAIN, 1.f);
541 					ALCheckErrorPrecise();
542 					al::qalFilterf(obstructionFilter, AL_LOWPASS_GAINHF, 0.1f);
543 					ALCheckErrorPrecise();
544 
545 					for (int i = 0; i < 128; i++) {
546 						roomHistory.push_back(20000.f);
547 						roomFeedbackHistory.push_back(0.f);
548 					}
549 				}
550 			}
~Internal()551 			~Internal() {
552 				SPADES_MARK_FUNCTION();
553 
554 				for (size_t i = 0; i < srcs.size(); i++)
555 					delete srcs[i];
556 			}
557 
AllocChunk()558 			ALSrc *AllocChunk() {
559 				SPADES_MARK_FUNCTION();
560 
561                 size_t start = SampleRandomInt<std::size_t>(0, srcs.size() - 1);
562 				for (size_t i = 0; i < srcs.size(); i++) {
563 					ALSrc *src = srcs[(i + start) % srcs.size()];
564 					if (src->IsPlaying())
565 						continue;
566 					return src;
567 				}
568 
569                 ALSrc *src = SampleRandomElement(srcs);
570 				src->Terminate();
571 				return src;
572 			}
573 
Play(ALAudioChunk * chunk,const Vector3 & origin,const client::AudioParam & param)574 			void Play(ALAudioChunk *chunk, const Vector3 &origin, const client::AudioParam &param) {
575 				SPADES_MARK_FUNCTION();
576 
577 				ALSrc *src = AllocChunk();
578 				if (!src)
579 					return;
580 
581 				src->stereo = chunk->GetFormat() == AL_FORMAT_STEREO16;
582 				src->SetParam(param);
583 				src->Set3D(origin);
584 				src->UpdateObstruction();
585 				src->PlayBufferOneShot(chunk->GetHandle());
586 			}
PlayLocal(ALAudioChunk * chunk,const Vector3 & origin,const client::AudioParam & param)587 			void PlayLocal(ALAudioChunk *chunk, const Vector3 &origin,
588 			               const client::AudioParam &param) {
589 				SPADES_MARK_FUNCTION();
590 
591 				ALSrc *src = AllocChunk();
592 				if (!src)
593 					return;
594 
595 				src->stereo = chunk->GetFormat() == AL_FORMAT_STEREO16;
596 				src->SetParam(param);
597 				src->Set3D(origin, true);
598 				src->UpdateObstruction();
599 				src->PlayBufferOneShot(chunk->GetHandle());
600 			}
PlayLocal(ALAudioChunk * chunk,const client::AudioParam & param)601 			void PlayLocal(ALAudioChunk *chunk, const client::AudioParam &param) {
602 				SPADES_MARK_FUNCTION();
603 
604 				ALSrc *src = AllocChunk();
605 				if (!src)
606 					return;
607 
608 				src->stereo = chunk->GetFormat() == AL_FORMAT_STEREO16;
609 				src->SetParam(param);
610 				src->Set2D();
611 				src->UpdateObstruction();
612 				src->PlayBufferOneShot(chunk->GetHandle());
613 			}
614 
Respatialize(const Vector3 & eye,const Vector3 & front,const Vector3 & up)615 			void Respatialize(const Vector3 &eye, const Vector3 &front, const Vector3 &up) {
616 				SPADES_MARK_FUNCTION();
617 
618 				float pos[] = {eye.x, eye.y, eye.z};
619 				float vel[] = {0, 0, 0};
620 				float orient[] = {front.x, front.y, front.z, up.x, up.y, up.z};
621 
622 				al::qalListenerfv(AL_POSITION, pos);
623 				ALCheckError();
624 				al::qalListenerfv(AL_VELOCITY, vel);
625 				ALCheckError();
626 				al::qalListenerfv(AL_ORIENTATION, orient);
627 				ALCheckError();
628 
629 				// do reverb simulation
630 				if (useEAX) {
631 					float maxDistance = 40.f;
632 					float reflections;
633 					float roomVolume;
634 					float roomArea;
635 					float roomSize;
636 					float feedbackness = 0.f;
637 					if (map == NULL) {
638 						reflections = 0.f;
639 						roomVolume = 1.f;
640 						roomArea = 1.f;
641 						roomSize = 10.f;
642 					} else {
643 						// do raycast
644 						Vector3 rayFrom = TransformVectorFromAL(eye);
645 						Vector3 rayTo;
646 
647 						for (int rays = 0; rays < 4; rays++) {
648 							rayTo.x = SampleRandomFloat() - SampleRandomFloat();
649 							rayTo.y = SampleRandomFloat() - SampleRandomFloat();
650 							rayTo.z = SampleRandomFloat() - SampleRandomFloat();
651 							rayTo = rayTo.Normalize();
652 
653 							IntVector3 hitPos;
654 							bool hit = map->CastRay(rayFrom, rayTo, maxDistance, hitPos);
655 							if (hit) {
656 								Vector3 hitPosf = {(float)hitPos.x, (float)hitPos.y,
657 								                   (float)hitPos.z};
658 								roomHistory[roomHistoryPos] = (hitPosf - rayFrom).GetLength();
659 							} else {
660 								roomHistory[roomHistoryPos] = maxDistance * 2.f;
661 							}
662 
663 							if (hit) {
664 								bool hit2 = map->CastRay(rayFrom, -rayTo, maxDistance, hitPos);
665 								if (hit2)
666 									roomFeedbackHistory[roomHistoryPos] = 1.f;
667 								else
668 									roomFeedbackHistory[roomHistoryPos] = 0.f;
669 							}
670 
671 							roomHistoryPos++;
672 							if (roomHistoryPos == (int)roomHistory.size())
673 								roomHistoryPos = 0;
674 						}
675 
676 						// monte-carlo integration
677 						unsigned int rayHitCount = 0;
678 						roomVolume = 0.f;
679 						roomArea = 0.f;
680 						roomSize = 0.f;
681 						feedbackness = 0.f;
682 						for (size_t i = 0; i < roomHistory.size(); i++) {
683 							float dist = roomHistory[i];
684 							if (dist < maxDistance) {
685 								rayHitCount++;
686 								roomVolume += dist * dist;
687 								roomArea += dist;
688 								roomSize += dist;
689 							}
690 
691 							feedbackness += roomFeedbackHistory[i];
692 						}
693 
694 						if (rayHitCount > roomHistory.size() / 4) {
695 							roomVolume /= (float)rayHitCount;
696 							roomVolume *= 4.f / 3.f * static_cast<float>(M_PI);
697 							roomArea /= (float)rayHitCount;
698 							roomArea *= 4.f * static_cast<float>(M_PI);
699 							roomSize /= (float)rayHitCount;
700 							reflections = (float)rayHitCount / (float)roomHistory.size();
701 						} else {
702 							roomVolume = 8.f;
703 							reflections = 0.2f;
704 							roomArea = 100.f;
705 							roomSize = 100.f;
706 						}
707 
708 						feedbackness /= (float)roomHistory.size();
709 					}
710 
711 					// printf("room size: %f, ref: %f, fb: %f\n", roomSize, reflections,
712 					// feedbackness);
713 
714 					EFXEAXREVERBPROPERTIES prop = EFX_REVERB_PRESET_ROOM;
715 					prop.flDecayTime = .161f * roomVolume / roomArea / .4f;
716 					prop.flGain = reflections * 0.8f;
717 					if (prop.flDecayTime > AL_EAXREVERB_MAX_DECAY_TIME)
718 						prop.flDecayTime = AL_EAXREVERB_MAX_DECAY_TIME;
719 					if (prop.flDecayTime < AL_EAXREVERB_MIN_DECAY_TIME)
720 						prop.flDecayTime = AL_EAXREVERB_MIN_DECAY_TIME;
721 					prop.flLateReverbDelay = roomSize / 324.f;
722 					if (prop.flLateReverbDelay > AL_EAXREVERB_MAX_LATE_REVERB_DELAY)
723 						prop.flLateReverbDelay = AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
724 					prop.flReflectionsDelay = roomSize / 324.f;
725 					if (prop.flReflectionsDelay > AL_EAXREVERB_MAX_REFLECTIONS_DELAY)
726 						prop.flReflectionsDelay = AL_EAXREVERB_MAX_REFLECTIONS_DELAY;
727 					prop.flLateReverbGain = reflections * reflections * reflections * reflections *
728 					                        feedbackness * feedbackness * feedbackness * .34f;
729 					prop.flReflectionsGain = reflections * .25f;
730 
731 					// printf("late: %f, ref: %f\n", prop.flLateReverbGain, prop.flReflectionsGain);
732 
733 					updateEFXReverb(&prop);
734 
735 					al::qalAuxiliaryEffectSloti(reverbFXSlot, AL_EFFECTSLOT_EFFECT, reverbFX);
736 					ALCheckError();
737 				}
738 
739 				for (size_t i = 0; i < srcs.size(); i++) {
740 					ALSrc *s = srcs[i];
741 					if ((SampleRandomInt(0, 7) == 0) && s->IsPlaying())
742 						s->UpdateObstruction();
743 				}
744 			}
745 		};
746 
ALDevice()747 		ALDevice::ALDevice() {
748 			SPADES_MARK_FUNCTION();
749 
750 			try {
751 				al::Link();
752 			} catch (...) {
753 				throw;
754 			}
755 			d = new Internal();
756 		}
757 
TryLoad()758 		bool ALDevice::TryLoad() {
759 			try {
760 				al::Link();
761 				return true;
762 			} catch (...) {
763 				return false;
764 			}
765 		}
766 
~ALDevice()767 		ALDevice::~ALDevice() {
768 			SPADES_MARK_FUNCTION();
769 
770 			delete d;
771 		}
CreateChunk(const char * name)772 		ALAudioChunk *ALDevice::CreateChunk(const char *name) {
773 			SPADES_MARK_FUNCTION();
774 
775 			IAudioStream *as = OpenAudioStream(name);
776 
777 			try {
778 				ALAudioChunk *ch = new ALAudioChunk(as);
779 				delete as;
780 				return ch;
781 			} catch (...) {
782 				delete as;
783 				throw;
784 			}
785 		}
786 
RegisterSound(const char * name)787 		client::IAudioChunk *ALDevice::RegisterSound(const char *name) {
788 			SPADES_MARK_FUNCTION();
789 
790 			std::map<std::string, ALAudioChunk *>::iterator it = chunks.find(name);
791 			if (it == chunks.end()) {
792 				ALAudioChunk *c = CreateChunk(name);
793 				chunks[name] = c;
794 				// release audiochunk later (to eliminate memory leak)
795 				return c;
796 			}
797 			it->second->AddRef();
798 			return it->second;
799 		}
800 
Play(client::IAudioChunk * chunk,const spades::Vector3 & origin,const client::AudioParam & param)801 		void ALDevice::Play(client::IAudioChunk *chunk, const spades::Vector3 &origin,
802 		                    const client::AudioParam &param) {
803 			SPADES_MARK_FUNCTION();
804 
805 			d->Play(static_cast<ALAudioChunk *>(chunk), TransformVectorToAL(origin), param);
806 		}
PlayLocal(client::IAudioChunk * chunk,const spades::Vector3 & origin,const client::AudioParam & param)807 		void ALDevice::PlayLocal(client::IAudioChunk *chunk, const spades::Vector3 &origin,
808 		                         const client::AudioParam &param) {
809 			SPADES_MARK_FUNCTION();
810 
811 			d->PlayLocal(static_cast<ALAudioChunk *>(chunk),
812 			             MakeVector3(origin.x, origin.y, -origin.z), param);
813 		}
814 
PlayLocal(client::IAudioChunk * chunk,const client::AudioParam & param)815 		void ALDevice::PlayLocal(client::IAudioChunk *chunk, const client::AudioParam &param) {
816 			SPADES_MARK_FUNCTION();
817 
818 			d->PlayLocal(static_cast<ALAudioChunk *>(chunk), param);
819 		}
820 
Respatialize(const spades::Vector3 & eye,const spades::Vector3 & front,const spades::Vector3 & up)821 		void ALDevice::Respatialize(const spades::Vector3 &eye, const spades::Vector3 &front,
822 		                            const spades::Vector3 &up) {
823 			SPADES_MARK_FUNCTION();
824 
825 			d->Respatialize(TransformVectorToAL(eye), TransformVectorToAL(front),
826 			                TransformVectorToAL(up));
827 		}
828 
SetGameMap(client::GameMap * mp)829 		void ALDevice::SetGameMap(client::GameMap *mp) {
830 			SPADES_MARK_FUNCTION_DEBUG();
831 			client::GameMap *oldMap = d->map;
832 			d->map = mp;
833 			if (mp)
834 				mp->AddRef();
835 			if (oldMap)
836 				oldMap->Release();
837 		}
838 	}
839 }
840