/* Copyright (c) 2013 yvt This file is part of OpenSpades. OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . */ #include #include #include #include #include #include "ALDevice.h" #include "ALFuncs.h" #include #include #include #include #include #include #include #include DEFINE_SPADES_SETTING(s_maxPolyphonics, "96"); DEFINE_SPADES_SETTING(s_eax, "1"); DEFINE_SPADES_SETTING(s_alPreciseErrorCheck, "1"); DEFINE_SPADES_SETTING(s_gain, "1"); // lm: seems to be missing for me.. #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 #endif #define ALCheckErrorPrecise() \ if (s_alPreciseErrorCheck) \ ALCheckError() namespace spades { namespace audio { std::uniform_real_distribution real_dist_audio(0, 1); static Vector3 TransformVectorToAL(Vector3 v) { return MakeVector3(v.x, v.y, v.z); } static Vector3 TransformVectorFromAL(Vector3 v) { return MakeVector3(v.x, v.y, v.z); } static float NextRandom() { return real_dist_audio(mt_engine); } namespace { std::vector ConvertFloatBufferToSignedShort(const std::vector &bytes) { if (bytes.size() & 3) { SPRaise("Size is invalid"); } std::vector ret; ret.resize(bytes.size() >> 1); for (size_t i = 0; i < bytes.size(); i += 4) { float inValue = reinterpret_cast(bytes.data())[i >> 2]; int16_t &outValue = reinterpret_cast(ret.data())[i >> 2]; outValue = static_cast(std::floor(inValue * 32768.f)); } return std::move(ret); } } class ALAudioChunk : public client::IAudioChunk { ALuint handle; ALuint format; protected: virtual ~ALAudioChunk() { SPADES_MARK_FUNCTION(); al::qalDeleteBuffers(1, &handle); ALCheckErrorPrecise(); } public: ALuint GetHandle() { return handle; } ALAudioChunk(IAudioStream *audioStream) { SPADES_MARK_FUNCTION(); std::vector bytes; if (audioStream->GetLength() > 128 * 1024 * 1024) { SPRaise("Audio stream too long"); } size_t len = (size_t)audioStream->GetLength(); bytes.resize(len); audioStream->SetPosition(0); if (audioStream->Read(bytes.data(), len) < len) SPRaise("Failed to read audio data"); ALuint alFormat; switch (audioStream->GetSampleFormat()) { case IAudioStream::UnsignedByte: switch (audioStream->GetNumChannels()) { case 1: alFormat = AL_FORMAT_MONO8; break; case 2: alFormat = AL_FORMAT_STEREO8; break; default: SPRaise("Unsupported audio format"); } break; case IAudioStream::SignedShort: switch (audioStream->GetNumChannels()) { case 1: alFormat = AL_FORMAT_MONO16; break; case 2: alFormat = AL_FORMAT_STEREO16; break; default: SPRaise("Unsupported audio format"); } break; case IAudioStream::SingleFloat: bytes = ConvertFloatBufferToSignedShort(bytes); switch (audioStream->GetNumChannels()) { case 1: alFormat = AL_FORMAT_MONO16; break; case 2: alFormat = AL_FORMAT_STEREO16; break; default: SPRaise("Unsupported audio format"); } break; default: SPRaise("Unsupported audio format"); } format = alFormat; al::qalGenBuffers(1, &handle); ALCheckError(); al::qalBufferData(handle, alFormat, bytes.data(), (ALuint)bytes.size(), audioStream->GetSamplingFrequency()); ALCheckError(); } ALuint GetFormat() { return format; } }; class ALDevice::Internal { public: bool useEAX; ALCdevice *alDevice; ALCcontext *alContext; ALuint reverbFXSlot; ALuint reverbFX; ALuint obstructionFilter; client::GameMap *map; struct ALSrc { Internal *internal; ALuint handle; bool eaxSource; bool stereo; bool local; client::AudioParam param; ALSrc(Internal *i) : internal(i) { SPADES_MARK_FUNCTION(); al::qalGenSources(1, &handle); ALCheckError(); } ~ALSrc() { SPADES_MARK_FUNCTION(); al::qalDeleteSources(1, &handle); ALCheckErrorPrecise(); } void Terminate() { SPADES_MARK_FUNCTION(); al::qalSourceStop(handle); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_BUFFER, 0); ALCheckError(); } bool IsPlaying() { SPADES_MARK_FUNCTION(); ALint value; al::qalGetSourcei(handle, AL_BUFFER, &value); ALCheckError(); if (value == 0) return false; al::qalGetSourcei(handle, AL_SOURCE_STATE, &value); ALCheckError(); if (value == AL_STOPPED) return false; return true; } // must be called void SetParam(const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); al::qalSourcef(handle, AL_PITCH, param.pitch); ALCheckErrorPrecise(); al::qalSourcef(handle, AL_GAIN, param.volume * std::max(std::min(s_gain, 4.0f), 0.0f)); ALCheckErrorPrecise(); al::qalSourcef(handle, AL_REFERENCE_DISTANCE, param.referenceDistance); ALCheckError(); this->param = param; } // either Set3D or Set2D must be called void Set3D(const Vector3 &v, bool local = false) { SPADES_MARK_FUNCTION(); ALfloat pos[] = {v.x, v.y, v.z}; ALfloat vel[] = {0, 0, 0}; al::qalSourcefv(handle, AL_POSITION, pos); ALCheckErrorPrecise(); al::qalSourcefv(handle, AL_VELOCITY, vel); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_SOURCE_RELATIVE, local ? AL_TRUE : AL_FALSE); ALCheckErrorPrecise(); al::qalSourcef(handle, AL_ROLLOFF_FACTOR, (local || stereo) ? 0.f : 1.f); ALCheckErrorPrecise(); if (internal->useEAX) { al::qalSource3i(handle, AL_AUXILIARY_SEND_FILTER, internal->reverbFXSlot, 0, AL_FILTER_NULL); ALCheckErrorPrecise(); } eaxSource = true; this->local = local; ALCheckError(); } void Set2D() { SPADES_MARK_FUNCTION(); ALfloat pos[] = {0, 0, 0}; ALfloat vel[] = {0, 0, 0}; al::qalSourcefv(handle, AL_POSITION, pos); ALCheckErrorPrecise(); al::qalSourcefv(handle, AL_VELOCITY, vel); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_SOURCE_RELATIVE, AL_TRUE); ALCheckErrorPrecise(); al::qalSourcef(handle, AL_ROLLOFF_FACTOR, 0.f); ALCheckErrorPrecise(); if (internal->useEAX) { al::qalSource3i(handle, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_DIRECT_FILTER, 0); ALCheckErrorPrecise(); } ALCheckError(); eaxSource = false; local = true; } // after calling Set2D/Set3D, must be called void UpdateObstruction() { SPADES_MARK_FUNCTION(); // update stereo source's volume (not spatialized by AL) // FIXME: move to another function? if (stereo && !local) { ALfloat v3[3]; al::qalGetListenerfv(AL_POSITION, v3); Vector3 eye = {v3[0], v3[1], v3[2]}; ALCheckErrorPrecise(); al::qalGetSourcefv(handle, AL_POSITION, v3); Vector3 pos = {v3[0], v3[1], v3[2]}; ALCheckErrorPrecise(); float dist = (pos - eye).GetLength(); dist /= param.referenceDistance; if (dist < 1.f) dist = 1.f; dist = 1.f / dist; al::qalSourcef(handle, AL_GAIN, param.volume * dist); ALCheckError(); } if (!internal->useEAX) return; ALint value; al::qalGetSourcei(handle, AL_SOURCE_RELATIVE, &value); ALCheckErrorPrecise(); bool enableObstruction = true; if (value) enableObstruction = false; // raytrace client::GameMap *map = internal->map; if (map && enableObstruction) { ALfloat v3[3]; al::qalGetListenerfv(AL_POSITION, v3); Vector3 eye = {v3[0], v3[1], v3[2]}; ALCheckErrorPrecise(); al::qalGetSourcefv(handle, AL_POSITION, v3); Vector3 pos = {v3[0], v3[1], v3[2]}; ALCheckErrorPrecise(); Vector3 checkPos; eye = TransformVectorFromAL(eye); pos = TransformVectorFromAL(pos); for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++) for (int z = -1; z <= 1; z++) { IntVector3 hitPos; checkPos.x = pos.x + (float)x * .2f; checkPos.y = pos.y + (float)y * .2f; checkPos.z = pos.z + (float)z * .2f; if (!map->CastRay(eye, (checkPos - eye).Normalize(), (checkPos - eye).GetLength(), hitPos)) { enableObstruction = false; } } } else { enableObstruction = false; } ALuint fx = AL_EFFECTSLOT_NULL; ALuint flt = AL_FILTER_NULL; if (enableObstruction) flt = internal->obstructionFilter; if (eaxSource) fx = internal->reverbFXSlot; al::qalSource3i(handle, AL_AUXILIARY_SEND_FILTER, fx, 0, flt); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_DIRECT_FILTER, flt); ALCheckError(); } void PlayBufferOneShot(ALuint buffer) { SPADES_MARK_FUNCTION(); al::qalSourcei(handle, AL_LOOPING, AL_FALSE); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_BUFFER, buffer); ALCheckErrorPrecise(); al::qalSourcei(handle, AL_SAMPLE_OFFSET, 0); ALCheckErrorPrecise(); al::qalSourcePlay(handle); ALCheckError(); } }; std::vector srcs; // reverb simulator int roomHistoryPos; std::vector roomHistory; std::vector roomFeedbackHistory; void updateEFXReverb(LPEFXEAXREVERBPROPERTIES reverb) { SPADES_MARK_FUNCTION_DEBUG(); al::qalEffectf(reverbFX, AL_EAXREVERB_DENSITY, reverb->flDensity); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_GAIN, reverb->flGain); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_GAINHF, reverb->flGainHF); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_GAINLF, reverb->flGainLF); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay); ALCheckErrorPrecise(); al::qalEffectfv(reverbFX, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay); ALCheckErrorPrecise(); al::qalEffectfv(reverbFX, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference); ALCheckErrorPrecise(); al::qalEffectf(reverbFX, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor); ALCheckErrorPrecise(); al::qalEffecti(reverbFX, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit); ALCheckErrorPrecise(); } Internal() { SPADES_MARK_FUNCTION(); if (al::qalGetString(AL_EXTENSIONS)) { std::vector strs = Split(al::qalGetString(AL_EXTENSIONS), " "); SPLog("OpenAL Extensions:"); for (size_t i = 0; i < strs.size(); i++) { SPLog(" %s", strs[i].c_str()); } } SPLog("--- All devices ---"); const ALCchar *ext = al::qalcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); while (ext && *ext) { SPLog("%s", ext); ext += (std::strlen(ext) + 1); } SPLog("-------------------"); SPLog("--- Devices ---"); ext = al::qalcGetString(NULL, ALC_DEVICE_SPECIFIER); while (ext && *ext) { SPLog("%s", ext); ext += (std::strlen(ext) + 1); } SPLog("---------------"); const ALCchar *dev = al::qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); if (dev && *dev) { SPLog("Default device: %s", dev); } alDevice = al::qalcOpenDevice(NULL); if (!alDevice) { if ((ext = al::qalcGetString(NULL, ALC_EXTENSIONS))) { std::vector strs = Split(ext, " "); SPLog("OpenAL ALC Extensions (NULL):"); for (size_t i = 0; i < strs.size(); i++) { SPLog(" %s", strs[i].c_str()); } } SPRaise("Failed to open OpenAL device."); } SPLog("OpenAL Info:"); SPLog(" Vendor: %s", al::qalGetString(AL_VENDOR)); SPLog(" Version: %s", al::qalGetString(AL_VERSION)); SPLog(" Renderer: %s", al::qalGetString(AL_RENDERER)); if ((ext = al::qalcGetString(alDevice, ALC_EXTENSIONS))) { std::vector strs = Split(ext, " "); SPLog("OpenAL ALC Extensions:"); for (size_t i = 0; i < strs.size(); i++) { SPLog(" %s", strs[i].c_str()); } } alContext = al::qalcCreateContext(alDevice, NULL); if (!alContext) { al::qalcCloseDevice(alDevice); SPRaise("Failed to create OpenAL context."); } al::qalcMakeContextCurrent(alContext); map = NULL; roomHistoryPos = 0; if (s_eax) { try { al::InitEAX(); useEAX = true; SPLog("EAX enabled"); } catch (const std::exception &ex) { useEAX = false; s_eax = 0; SPLog("Failed to initialize EAX: \n%s", ex.what()); } } else { SPLog("EAX is disabled by configuration (s_eax)"); useEAX = false; } // somehow Apple OpenAL reports an error whose source is unknown... // so we clear that now while (al::qalGetError() != AL_NO_ERROR) ; ALCheckError(); for (int i = 0; i < (int)s_maxPolyphonics; i++) { srcs.push_back(new ALSrc(this)); } SPLog("%d source(s) initialized", (int)s_maxPolyphonics); al::qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); ALCheckErrorPrecise(); // initialize EAX if (useEAX) { al::qalGenAuxiliaryEffectSlots(1, &reverbFXSlot); ALCheckError(); al::qalGenEffects(1, &reverbFX); ALCheckError(); al::qalEffecti(reverbFX, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); ALCheckError(); EFXEAXREVERBPROPERTIES prop = EFX_REVERB_PRESET_MOUNTAINS; updateEFXReverb(&prop); al::qalAuxiliaryEffectSloti(reverbFXSlot, AL_EFFECTSLOT_EFFECT, reverbFX); ALCheckError(); al::qalGenFilters(1, &obstructionFilter); ALCheckErrorPrecise(); al::qalFilteri(obstructionFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); ALCheckErrorPrecise(); al::qalFilterf(obstructionFilter, AL_LOWPASS_GAIN, 1.f); ALCheckErrorPrecise(); al::qalFilterf(obstructionFilter, AL_LOWPASS_GAINHF, 0.1f); ALCheckErrorPrecise(); for (int i = 0; i < 128; i++) { roomHistory.push_back(20000.f); roomFeedbackHistory.push_back(0.f); } } } ~Internal() { SPADES_MARK_FUNCTION(); for (size_t i = 0; i < srcs.size(); i++) delete srcs[i]; } ALSrc *AllocChunk() { SPADES_MARK_FUNCTION(); size_t start = mt_engine() % srcs.size(); for (size_t i = 0; i < srcs.size(); i++) { ALSrc *src = srcs[(i + start) % srcs.size()]; if (src->IsPlaying()) continue; return src; } ALSrc *src = srcs[mt_engine() % srcs.size()]; src->Terminate(); return src; } void Play(ALAudioChunk *chunk, const Vector3 &origin, const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); ALSrc *src = AllocChunk(); if (!src) return; src->stereo = chunk->GetFormat() == AL_FORMAT_STEREO16; src->SetParam(param); src->Set3D(origin); src->UpdateObstruction(); src->PlayBufferOneShot(chunk->GetHandle()); } void PlayLocal(ALAudioChunk *chunk, const Vector3 &origin, const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); ALSrc *src = AllocChunk(); if (!src) return; src->stereo = chunk->GetFormat() == AL_FORMAT_STEREO16; src->SetParam(param); src->Set3D(origin, true); src->UpdateObstruction(); src->PlayBufferOneShot(chunk->GetHandle()); } void PlayLocal(ALAudioChunk *chunk, const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); ALSrc *src = AllocChunk(); if (!src) return; src->stereo = chunk->GetFormat() == AL_FORMAT_STEREO16; src->SetParam(param); src->Set2D(); src->UpdateObstruction(); src->PlayBufferOneShot(chunk->GetHandle()); } void Respatialize(const Vector3 &eye, const Vector3 &front, const Vector3 &up) { SPADES_MARK_FUNCTION(); float pos[] = {eye.x, eye.y, eye.z}; float vel[] = {0, 0, 0}; float orient[] = {front.x, front.y, front.z, up.x, up.y, up.z}; al::qalListenerfv(AL_POSITION, pos); ALCheckError(); al::qalListenerfv(AL_VELOCITY, vel); ALCheckError(); al::qalListenerfv(AL_ORIENTATION, orient); ALCheckError(); // do reverb simulation if (useEAX) { float maxDistance = 40.f; float reflections; float roomVolume; float roomArea; float roomSize; float feedbackness = 0.f; if (map == NULL) { reflections = 0.f; roomVolume = 1.f; roomArea = 1.f; roomSize = 10.f; } else { // do raycast Vector3 rayFrom = TransformVectorFromAL(eye); Vector3 rayTo; for (int rays = 0; rays < 4; rays++) { rayTo.x = NextRandom() - NextRandom(); rayTo.y = NextRandom() - NextRandom(); rayTo.z = NextRandom() - NextRandom(); rayTo = rayTo.Normalize(); IntVector3 hitPos; bool hit = map->CastRay(rayFrom, rayTo, maxDistance, hitPos); if (hit) { Vector3 hitPosf = {(float)hitPos.x, (float)hitPos.y, (float)hitPos.z}; roomHistory[roomHistoryPos] = (hitPosf - rayFrom).GetLength(); } else { roomHistory[roomHistoryPos] = maxDistance * 2.f; } if (hit) { bool hit2 = map->CastRay(rayFrom, -rayTo, maxDistance, hitPos); if (hit2) roomFeedbackHistory[roomHistoryPos] = 1.f; else roomFeedbackHistory[roomHistoryPos] = 0.f; } roomHistoryPos++; if (roomHistoryPos == (int)roomHistory.size()) roomHistoryPos = 0; } // monte-carlo integration unsigned int rayHitCount = 0; roomVolume = 0.f; roomArea = 0.f; roomSize = 0.f; feedbackness = 0.f; for (size_t i = 0; i < roomHistory.size(); i++) { float dist = roomHistory[i]; if (dist < maxDistance) { rayHitCount++; roomVolume += dist * dist; roomArea += dist; roomSize += dist; } feedbackness += roomFeedbackHistory[i]; } if (rayHitCount > roomHistory.size() / 4) { roomVolume /= (float)rayHitCount; roomVolume *= 4.f / 3.f * static_cast(M_PI); roomArea /= (float)rayHitCount; roomArea *= 4.f * static_cast(M_PI); roomSize /= (float)rayHitCount; reflections = (float)rayHitCount / (float)roomHistory.size(); } else { roomVolume = 8.f; reflections = 0.2f; roomArea = 100.f; roomSize = 100.f; } feedbackness /= (float)roomHistory.size(); } // printf("room size: %f, ref: %f, fb: %f\n", roomSize, reflections, // feedbackness); EFXEAXREVERBPROPERTIES prop = EFX_REVERB_PRESET_ROOM; prop.flDecayTime = .161f * roomVolume / roomArea / .4f; prop.flGain = reflections * 0.8f; if (prop.flDecayTime > AL_EAXREVERB_MAX_DECAY_TIME) prop.flDecayTime = AL_EAXREVERB_MAX_DECAY_TIME; if (prop.flDecayTime < AL_EAXREVERB_MIN_DECAY_TIME) prop.flDecayTime = AL_EAXREVERB_MIN_DECAY_TIME; prop.flLateReverbDelay = roomSize / 324.f; if (prop.flLateReverbDelay > AL_EAXREVERB_MAX_LATE_REVERB_DELAY) prop.flLateReverbDelay = AL_EAXREVERB_MAX_LATE_REVERB_DELAY; prop.flReflectionsDelay = roomSize / 324.f; if (prop.flReflectionsDelay > AL_EAXREVERB_MAX_REFLECTIONS_DELAY) prop.flReflectionsDelay = AL_EAXREVERB_MAX_REFLECTIONS_DELAY; prop.flLateReverbGain = reflections * reflections * reflections * reflections * feedbackness * feedbackness * feedbackness * .34f; prop.flReflectionsGain = reflections * .25f; // printf("late: %f, ref: %f\n", prop.flLateReverbGain, prop.flReflectionsGain); updateEFXReverb(&prop); al::qalAuxiliaryEffectSloti(reverbFXSlot, AL_EFFECTSLOT_EFFECT, reverbFX); ALCheckError(); } for (size_t i = 0; i < srcs.size(); i++) { ALSrc *s = srcs[i]; if ((mt_engine() % 8 == 0) && s->IsPlaying()) s->UpdateObstruction(); } } }; ALDevice::ALDevice() { SPADES_MARK_FUNCTION(); try { al::Link(); } catch (...) { throw; } d = new Internal(); } bool ALDevice::TryLoad() { try { al::Link(); return true; } catch (...) { return false; } } ALDevice::~ALDevice() { SPADES_MARK_FUNCTION(); delete d; } ALAudioChunk *ALDevice::CreateChunk(const char *name) { SPADES_MARK_FUNCTION(); IAudioStream *as = OpenAudioStream(name); try { ALAudioChunk *ch = new ALAudioChunk(as); delete as; return ch; } catch (...) { delete as; throw; } } client::IAudioChunk *ALDevice::RegisterSound(const char *name) { SPADES_MARK_FUNCTION(); std::map::iterator it = chunks.find(name); if (it == chunks.end()) { ALAudioChunk *c = CreateChunk(name); chunks[name] = c; // release audiochunk later (to eliminate memory leak) return c; } it->second->AddRef(); return it->second; } void ALDevice::Play(client::IAudioChunk *chunk, const spades::Vector3 &origin, const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); d->Play(static_cast(chunk), TransformVectorToAL(origin), param); } void ALDevice::PlayLocal(client::IAudioChunk *chunk, const spades::Vector3 &origin, const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); d->PlayLocal(static_cast(chunk), MakeVector3(origin.x, origin.y, -origin.z), param); } void ALDevice::PlayLocal(client::IAudioChunk *chunk, const client::AudioParam ¶m) { SPADES_MARK_FUNCTION(); d->PlayLocal(static_cast(chunk), param); } void ALDevice::Respatialize(const spades::Vector3 &eye, const spades::Vector3 &front, const spades::Vector3 &up) { SPADES_MARK_FUNCTION(); d->Respatialize(TransformVectorToAL(eye), TransformVectorToAL(front), TransformVectorToAL(up)); } void ALDevice::SetGameMap(client::GameMap *mp) { SPADES_MARK_FUNCTION_DEBUG(); client::GameMap *oldMap = d->map; d->map = mp; if (mp) mp->AddRef(); if (oldMap) oldMap->Release(); } } }