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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m) { 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