diff --git a/Engine/source/T3D/levelInfo.cpp b/Engine/source/T3D/levelInfo.cpp index 884fd7d17..41f4328e4 100644 --- a/Engine/source/T3D/levelInfo.cpp +++ b/Engine/source/T3D/levelInfo.cpp @@ -217,7 +217,7 @@ U32 LevelInfo::packUpdate(NetConnection *conn, U32 mask, BitStream *stream) mathWrite( *stream, mAmbientLightBlendCurve ); sfxWrite( stream, mSoundAmbience ); - stream->writeInt( mSoundDistanceModel, 1 ); + stream->writeInt( mSoundDistanceModel, 4 ); PACK_ASSET_REFACTOR(conn, AccuTexture); @@ -251,7 +251,7 @@ void LevelInfo::unpackUpdate(NetConnection *conn, BitStream *stream) String errorStr; if( !sfxReadAndResolve( stream, &mSoundAmbience, errorStr ) ) Con::errorf( "%s", errorStr.c_str() ); - mSoundDistanceModel = ( SFXDistanceModel ) stream->readInt( 1 ); + mSoundDistanceModel = ( SFXDistanceModel ) stream->readInt( 4 ); if( isProperlyAdded() ) { diff --git a/Engine/source/sfx/openal/sfxALDevice.cpp b/Engine/source/sfx/openal/sfxALDevice.cpp index 6a090fb09..6e75d754d 100644 --- a/Engine/source/sfx/openal/sfxALDevice.cpp +++ b/Engine/source/sfx/openal/sfxALDevice.cpp @@ -49,6 +49,14 @@ #define FUNCTION_CAST(T, ptr) (T)(ptr) #endif +#define SFX_LOAD_AL_PROC(T, x) \ + (x) = FUNCTION_CAST(T, alGetProcAddress(#x)); \ + if (!(x)) Con::warnf("SFXALDevice - Failed to load " #x); + +#define SFX_LOAD_ALC_PROC(T, x) \ + (x) = FUNCTION_CAST(T, alcGetProcAddress(mDevice, #x)); \ + if (!(x)) Con::warnf("SFXALDevice - Failed to load " #x); + /* Effect object functions */ static LPALGENFILTERS alGenFilters{ nullptr }; static LPALDELETEFILTERS alDeleteFilters{ nullptr }; @@ -107,57 +115,287 @@ SFXDevice* SFXALDevice::createInstance(U32 providerIndex) return dev; } +void SFXALDevice::_initExtensions() +{ + // Device-level soft extensions — must be loaded before context creation + if (alcIsExtensionPresent(mDevice, "ALC_SOFT_reopen_device")) + { + SFX_LOAD_ALC_PROC(LPALCREOPENDEVICESOFT, alcReopenDeviceSOFT); + SFX_LOAD_ALC_PROC(LPALCRESETDEVICESOFT, alcResetDeviceSOFT); + if (alcReopenDeviceSOFT && alcResetDeviceSOFT) + { + mHasSoftReopen = true; + mCaps |= CAPS_HotReconnect; + } + } + + if (alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF")) + { + SFX_LOAD_ALC_PROC(LPALCGETSTRINGISOFT, alcGetStringiSOFT); + if (alcGetStringiSOFT) + { + mHasSoftHRTF = true; + mCaps |= CAPS_HRTF; + } + } + + if (alcIsExtensionPresent(mDevice, "ALC_SOFT_device_clock")) + { + SFX_LOAD_ALC_PROC(LPALCGETINTEGER64VSOFT, alcGetInteger64vSOFT); + if (alcGetInteger64vSOFT) + mCaps |= CAPS_DeviceClock; + } + + if (alcIsExtensionPresent(mDevice, "ALC_EXT_disconnect")) + mCaps |= CAPS_DisconnectDetect; + + // Context-level AL extensions — loaded after context is current + if (alIsExtensionPresent("AL_EXT_float32")) + mCaps |= CAPS_Float32; + if (alIsExtensionPresent("AL_EXT_MCFORMATS")) + mCaps |= CAPS_MonoStereo; + if (alIsExtensionPresent("AL_SOFT_source_spatialize")) + mCaps |= CAPS_SourceSpatialize; +} + +void SFXALDevice::_initEFX() +{ + if (alcIsExtensionPresent(mDevice, "ALC_EXT_EFX") != AL_TRUE) + return; + + // Load all EFX procs + SFX_LOAD_AL_PROC(LPALGENEFFECTS, alGenEffects); + SFX_LOAD_AL_PROC(LPALDELETEEFFECTS, alDeleteEffects); + SFX_LOAD_AL_PROC(LPALISEFFECT, alIsEffect); + SFX_LOAD_AL_PROC(LPALEFFECTI, alEffecti); + SFX_LOAD_AL_PROC(LPALEFFECTIV, alEffectiv); + SFX_LOAD_AL_PROC(LPALEFFECTF, alEffectf); + SFX_LOAD_AL_PROC(LPALEFFECTFV, alEffectfv); + SFX_LOAD_AL_PROC(LPALGETEFFECTI, alGetEffecti); + SFX_LOAD_AL_PROC(LPALGETEFFECTIV, alGetEffectiv); + SFX_LOAD_AL_PROC(LPALGETEFFECTF, alGetEffectf); + SFX_LOAD_AL_PROC(LPALGETEFFECTFV, alGetEffectfv); + SFX_LOAD_AL_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); + SFX_LOAD_AL_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); + SFX_LOAD_AL_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); + SFX_LOAD_AL_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); + SFX_LOAD_AL_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); + SFX_LOAD_AL_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); + SFX_LOAD_AL_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); + SFX_LOAD_AL_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); + SFX_LOAD_AL_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); + SFX_LOAD_AL_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); + SFX_LOAD_AL_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); + SFX_LOAD_AL_PROC(LPALGENFILTERS, alGenFilters); + SFX_LOAD_AL_PROC(LPALDELETEFILTERS, alDeleteFilters); + SFX_LOAD_AL_PROC(LPALISFILTER, alIsFilter); + SFX_LOAD_AL_PROC(LPALFILTERI, alFilteri); + SFX_LOAD_AL_PROC(LPALFILTERIV, alFilteriv); + SFX_LOAD_AL_PROC(LPALFILTERF, alFilterf); + SFX_LOAD_AL_PROC(LPALFILTERFV, alFilterfv); + SFX_LOAD_AL_PROC(LPALGETFILTERI, alGetFilteri); + SFX_LOAD_AL_PROC(LPALGETFILTERIV, alGetFilteriv); + SFX_LOAD_AL_PROC(LPALGETFILTERF, alGetFilterf); + SFX_LOAD_AL_PROC(LPALGETFILTERFV, alGetFilterfv); + + // Verify the minimum set we can't live without + if (!alGenEffects || !alDeleteEffects || + !alGenAuxiliaryEffectSlots || !alDeleteAuxiliaryEffectSlots || + !alAuxiliaryEffectSloti) + { + Con::warnf("SFXALDevice - EFX proc loading incomplete, disabling reverb"); + return; + } + + mCaps |= CAPS_Reverb | CAPS_Occlusion; + + // Filters require their own procs + if (alGenFilters && alDeleteFilters && alFilteri && alFilterf) + mCaps |= CAPS_SourceFilters; + + // Probe individual effect type support and slot count + _probeEFXEffectCaps(); +} + +void SFXALDevice::_probeEFXEffectCaps() +{ + // Probe which effect types this driver actually supports + // by attempting to set each type on a temporary effect object. + // Some drivers report ALC_EXT_EFX but don't implement all types. + + struct EffectProbe { ALenum alType; U32 cap; const char* name; }; + static const EffectProbe probes[] = + { + { AL_EFFECT_EAXREVERB, CAPS_Reverb | CAPS_EXTReverb, "EAX Reverb" }, + { AL_EFFECT_REVERB, CAPS_Reverb, "EAXReverb" }, + { AL_EFFECT_EQUALIZER, CAPS_EFX_EQ, "Equalizer" }, + { AL_EFFECT_COMPRESSOR, CAPS_EFX_Compressor,"Compressor" }, + { AL_EFFECT_ECHO, CAPS_EFX_Echo, "Echo" }, + { AL_EFFECT_CHORUS, CAPS_EFX_Chorus, "Chorus" }, + { AL_EFFECT_DISTORTION, CAPS_EFX_Distortion,"Distortion" }, + { AL_EFFECT_FLANGER, CAPS_EFX_Flanger, "Flanger" }, + }; + + ALuint testEffect = 0; + alGenEffects(1, &testEffect); + + for (const EffectProbe& probe : probes) + { + alGetError(); + alEffecti(testEffect, AL_EFFECT_TYPE, probe.alType); + if (alGetError() == AL_NO_ERROR) + mCaps |= probe.cap; + } + + alDeleteEffects(1, &testEffect); + alGetError(); + + // Probe aux slot count + Vector slots; + alGetError(); + while (slots.size() < 64) + { + ALuint slot = 0; + alGenAuxiliaryEffectSlots(1, &slot); + if (alGetError() != AL_NO_ERROR) + break; + slots.push_back(slot); + } + + Con::setIntVariable("$pref::SFX::maxEffectSlots", (S32)slots.size()); + + ALCint sends = 0; + alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &sends); + Con::setIntVariable("$pref::SFX::maxSendsPerSource", (S32)sends); + + for (ALuint slot : slots) + alDeleteAuxiliaryEffectSlots(1, &slot); + alGetError(); + + // Allocate the device-level global reverb slot + alGenAuxiliaryEffectSlots(1, &mAuxSlot); + if (alGetError() != AL_NO_ERROR) + { + Con::warnf("SFXALDevice - Failed to create primary EFX slot"); + mCaps &= ~(CAPS_Reverb | CAPS_Occlusion); + mAuxSlot = 0; + } +} //---------------------------------------------------------------------------- // STATIC OPENAL FUNCTIONS //---------------------------------------------------------------------------- void SFXALDevice::printALInfo(ALCdevice* device) { + if (!device) + return; + + Con::printBlankLine(); + Con::printf("SFX Device Info:"); + Con::printf("|------------------------------------------------"); + + // --- Device name --- + const ALCchar* devname = alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT") + ? alcGetString(device, ALC_ALL_DEVICES_SPECIFIER) + : alcGetString(device, ALC_DEVICE_SPECIFIER); + Con::printf("| Device: %s", devname); + + // --- OpenAL version --- ALCint major, minor; - if (device) - { - const ALCchar* devname = NULL; - Con::printBlankLine(); - - if (alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) - { - devname = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER); - } - else - { - devname = alcGetString(device, ALC_DEVICE_SPECIFIER); - } - - Con::printf("| Device info for: %s ", devname); - } - alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major); alcGetIntegerv(device, ALC_MINOR_VERSION, 1, &minor); - Con::printf("| OpenAL Version: %d.%d", major, minor); + Con::printf("| OpenAL: %d.%d", major, minor); - if (device) + // --- Audio format --- + ALCint freq = 0; + alcGetIntegerv(device, ALC_FREQUENCY, 1, &freq); + Con::printf("| Sample Rate: %d Hz", freq > 0 ? freq : 44100); + Con::printf("| Bit Depth: %d-bit", (mCaps & CAPS_Float32) ? 32 : 16); + Con::printf("| Max Voices: %d", mMaxBuffers); + + Con::printf("|------------------------------------------------"); + Con::printf("| Capabilities:"); + + // --- Hot reconnect --- + Con::printf("| Hot Reconnect: %s", (mCaps & CAPS_HotReconnect) ? "Yes" : "No"); + Con::printf("| Disconnect Detect: %s", (mCaps & CAPS_DisconnectDetect) ? "Yes" : "No"); + Con::printf("| Device Clock: %s", (mCaps & CAPS_DeviceClock) ? "Yes" : "No"); + Con::printf("| Multi-Listener: %s", (mCaps & CAPS_MultiListener) ? "Yes" : "No"); + Con::printf("| Float32 Playback: %s", (mCaps & CAPS_Float32) ? "Yes" : "No"); + Con::printf("| Multi-Channel: %s", (mCaps & CAPS_MonoStereo) ? "Yes" : "No"); + Con::printf("| Spatialize: %s", (mCaps & CAPS_SourceSpatialize) ? "Yes" : "No"); + + // --- HRTF --- + Con::printf("| HRTF: %s", (mCaps & CAPS_HRTF) ? "Yes" : "No"); + if (mCaps & CAPS_HRTF) { - const ALchar* extStr = alcGetString(device, ALC_EXTENSIONS); - if (extStr) + ALCint hrtfStatus = 0; + alcGetIntegerv(device, ALC_HRTF_STATUS_SOFT, 1, &hrtfStatus); + + const char* statusStr = "Unknown"; + switch (hrtfStatus) { - Con::printf("| SFXALDevice - Supported ALC extensions:"); - - // Copy to a modifiable string. - char* extCopy = dStrdup(extStr); - char* token = dStrtok(extCopy, " "); - while (token) - { - Con::printf("| %s", token); - token = dStrtok(NULL, " "); - } - - dFree(extCopy); + case ALC_HRTF_DISABLED_SOFT: statusStr = "Disabled"; break; + case ALC_HRTF_ENABLED_SOFT: statusStr = "Enabled"; break; + case ALC_HRTF_DENIED_SOFT: statusStr = "Denied by device"; break; + case ALC_HRTF_REQUIRED_SOFT: statusStr = "Required by device"; break; + case ALC_HRTF_HEADPHONES_DETECTED_SOFT: statusStr = "Headphones detected"; break; + case ALC_HRTF_UNSUPPORTED_FORMAT_SOFT: statusStr = "Unsupported format"; break; } + Con::printf("| Status: %s", statusStr); - U32 err = alcGetError(device); - if (err != ALC_NO_ERROR) - Con::errorf("SFXALDevice - Error Retrieving ALC Extensions: %s", alcGetString(device, err)); + ALCint profileCount = 0; + alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &profileCount); + Con::printf("| Profiles: %d", profileCount); + + if (alcGetStringiSOFT && profileCount > 0) + for (ALCint i = 0; i < profileCount; i++) + Con::printf("| [%d] %s", i, + alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i)); } + + // --- EFX --- + Con::printf("| EFX Reverb: %s", (mCaps & CAPS_Reverb) ? "Yes" : "No"); + if (mCaps & CAPS_Reverb) + { + Con::printf("| EQ: %s", (mCaps & CAPS_EFX_EQ) ? "Yes" : "No"); + Con::printf("| Compressor: %s", (mCaps & CAPS_EFX_Compressor) ? "Yes" : "No"); + Con::printf("| Echo: %s", (mCaps & CAPS_EFX_Echo) ? "Yes" : "No"); + Con::printf("| Chorus: %s", (mCaps & CAPS_EFX_Chorus) ? "Yes" : "No"); + Con::printf("| Distortion: %s", (mCaps & CAPS_EFX_Distortion) ? "Yes" : "No"); + Con::printf("| Flanger: %s", (mCaps & CAPS_EFX_Flanger) ? "Yes" : "No"); + Con::printf("| Filters: %s", (mCaps & CAPS_SourceFilters) ? "Yes" : "No"); + + Con::printf("| Effect Slots: %d", + Con::getIntVariable("$pref::SFX::maxEffectSlots")); + Con::printf("| Sends/Source: %d", + Con::getIntVariable("$pref::SFX::maxSendsPerSource")); + } + + Con::printf("|------------------------------------------------"); + + // --- Full extension list last so it doesn't bury the important info --- + const ALchar* extStr = alcGetString(device, ALC_EXTENSIONS); + if (extStr) + { + Con::printf("| ALC Extensions:"); + char* extCopy = dStrdup(extStr); + char* token = dStrtok(extCopy, " "); + while (token) + { + Con::printf("| %s", token); + token = dStrtok(NULL, " "); + } + dFree(extCopy); + } + + Con::printf("|------------------------------------------------"); + Con::printBlankLine(); + + U32 err = alcGetError(device); + if (err != ALC_NO_ERROR) + Con::errorf("SFXALDevice - ALC error after info query: %s", + alcGetString(device, err)); } @@ -210,95 +448,56 @@ SFXALDevice::SFXALDevice(U32 providerIndex) mRolloffFactor(1.0f), mUserRolloffFactor(1.0f), mHasEFX(false), + mHasSoftReopen(false), + mHasSoftHRTF(false), mEffect(0), mAuxSlot(0) { SFXProvider* p = SFXSystem::getProvider(providerIndex); mDevice = alcOpenDevice(p->getName()); - - U32 err = alcGetError(mDevice); - if (err != ALC_NO_ERROR) - Con::errorf("SFXALDevice - Device Initialization Error: %s", alcGetString(mDevice, err)); - - if (mDevice) + if (!mDevice) { - mContext = alcCreateContext(mDevice, NULL); - - if (mContext) - alcMakeContextCurrent(mContext); - - if( err != ALC_NO_ERROR ) - Con::errorf( "SFXALDevice - Context Initialization Error: %s", alcGetString( mDevice, err ) ); + Con::errorf("SFXALDevice - Failed to open '%s'", p->getName()); + return; } - AssertFatal( mDevice != NULL && mContext != NULL, "Failed to create OpenAL device and/or context!" ); + // Extensions that must be loaded before context creation + _initExtensions(); - // Start the update thread. - // TODO AsyncPeriodicUpdateThread support for Linux/Mac -#ifdef TORQUE_OS_WIN - if( !Con::getBoolVariable( "$_forceAllMainThread" ) ) + mContext = alcCreateContext(mDevice, NULL); + if (!mContext) { - SFXInternal::gUpdateThread = new AsyncPeriodicUpdateThread - ( "OpenAL Update Thread", SFXInternal::gBufferUpdateList, - Con::getIntVariable( "$pref::SFX::updateInterval", SFXInternal::DEFAULT_UPDATE_INTERVAL ) ); + Con::errorf("SFXALDevice - Failed to create context (error %d)", + alcGetError(mDevice)); + alcCloseDevice(mDevice); + mDevice = NULL; + return; + } + + alcMakeContextCurrent(mContext); + +#ifdef TORQUE_OS_WIN + + if (!Con::getBoolVariable("$_forceAllMainThread")) + { + SFXInternal::gUpdateThread = new AsyncPeriodicUpdateThread( + "OpenAL Update Thread", SFXInternal::gBufferUpdateList, + Con::getIntVariable("$pref::SFX::updateInterval", + SFXInternal::DEFAULT_UPDATE_INTERVAL)); SFXInternal::gUpdateThread->start(); } #endif - // --- Capabilities --- - if (alcIsExtensionPresent(mDevice, "ALC_EXT_EFX") == AL_TRUE) - mCaps |= CAPS_Reverb | CAPS_Occlusion; - if (alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF") == AL_TRUE) - mCaps |= CAPS_HRTF; - if (alIsExtensionPresent("AL_EXT_float32") == AL_TRUE) - mCaps |= CAPS_Float32; - if (alIsExtensionPresent("AL_EXT_MCFORMATS") == AL_TRUE) - mCaps |= CAPS_MonoStereo; + // Context is current — load context-level extensions and EFX + _initExtensions(); // second pass picks up AL_* extensions now context is current + _initEFX(); - if (mCaps & CAPS_Reverb) - { -#define LOAD_PROC(T, x) ((x) = FUNCTION_CAST(T, alGetProcAddress(#x))) - LOAD_PROC(LPALGENEFFECTS, alGenEffects); - LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects); - LOAD_PROC(LPALISEFFECT, alIsEffect); - LOAD_PROC(LPALEFFECTI, alEffecti); - LOAD_PROC(LPALEFFECTIV, alEffectiv); - LOAD_PROC(LPALEFFECTF, alEffectf); - LOAD_PROC(LPALEFFECTFV, alEffectfv); - LOAD_PROC(LPALGETEFFECTI, alGetEffecti); - LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv); - LOAD_PROC(LPALGETEFFECTF, alGetEffectf); - LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv); - - LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); - LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); - LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); - LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); - LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); -#undef LOAD_PROC - - // generate our auxiliary slot for the effects. - alGenAuxiliaryEffectSlots(1, &mAuxSlot); - } - - // --- Device frequency --- + // Read back what the device actually settled on ALCint freq = 0; alcGetIntegerv(mDevice, ALC_FREQUENCY, 1, &freq); - if (freq > 0) - Con::setIntVariable("$pref::SFX::frequency", freq); - else - Con::setIntVariable("$pref::SFX::frequency", 44100); // default - - // --- Bitrate approximation --- - U32 bitrate = (mCaps & CAPS_Float32) ? 32 : 16; - Con::setIntVariable("$pref::SFX::bitrate", bitrate); + Con::setIntVariable("$pref::SFX::frequency", freq > 0 ? freq : 44100); + Con::setIntVariable("$pref::SFX::bitrate", (mCaps & CAPS_Float32) ? 32 : 16); printALInfo(mDevice); @@ -463,8 +662,6 @@ void SFXALDevice::setReverb(const SFXReverbProperties& r) if (!(mCaps & CAPS_Reverb)) return; - ALenum err; - /* Clear error state. */ alGetError(); @@ -473,70 +670,74 @@ void SFXALDevice::setReverb(const SFXReverbProperties& r) /* Create the effect object and check if we can do EAX reverb. */ alGenEffects(1, &mEffect); - alEffecti(mEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - err = alGetError(); // Map your engine's properties to EFX parameters. // Using EAX Reverb as an example: - if (err == AL_NO_ERROR) + if (mCaps & CAPS_EXTReverb) { - alEffectf(mEffect, AL_EAXREVERB_DENSITY, r.flDensity); - alEffectf(mEffect, AL_EAXREVERB_DIFFUSION, r.flDiffusion); - alEffectf(mEffect, AL_EAXREVERB_GAIN, r.flGain); - alEffectf(mEffect, AL_EAXREVERB_GAINHF, r.flGainHF); - alEffectf(mEffect, AL_EAXREVERB_GAINLF, r.flGainLF); - alEffectf(mEffect, AL_EAXREVERB_DECAY_TIME, r.flDecayTime); - alEffectf(mEffect, AL_EAXREVERB_DECAY_HFRATIO, r.flDecayHFRatio); - alEffectf(mEffect, AL_EAXREVERB_DECAY_LFRATIO, r.flDecayLFRatio); + // Full EAX parameter set — panning vectors, LF params, modulation etc. + alEffecti(mEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + if (alGetError() == AL_NO_ERROR) + { + alEffectf(mEffect, AL_EAXREVERB_DENSITY, r.flDensity); + alEffectf(mEffect, AL_EAXREVERB_DIFFUSION, r.flDiffusion); + alEffectf(mEffect, AL_EAXREVERB_GAIN, r.flGain); + alEffectf(mEffect, AL_EAXREVERB_GAINHF, r.flGainHF); + alEffectf(mEffect, AL_EAXREVERB_GAINLF, r.flGainLF); + alEffectf(mEffect, AL_EAXREVERB_DECAY_TIME, r.flDecayTime); + alEffectf(mEffect, AL_EAXREVERB_DECAY_HFRATIO, r.flDecayHFRatio); + alEffectf(mEffect, AL_EAXREVERB_DECAY_LFRATIO, r.flDecayLFRatio); - alEffectf(mEffect, AL_EAXREVERB_REFLECTIONS_GAIN, r.flReflectionsGain); - alEffectf(mEffect, AL_EAXREVERB_REFLECTIONS_DELAY, r.flReflectionsDelay); - alEffectfv(mEffect, AL_EAXREVERB_REFLECTIONS_PAN, r.flReflectionsPan); + alEffectf(mEffect, AL_EAXREVERB_REFLECTIONS_GAIN, r.flReflectionsGain); + alEffectf(mEffect, AL_EAXREVERB_REFLECTIONS_DELAY, r.flReflectionsDelay); + alEffectfv(mEffect, AL_EAXREVERB_REFLECTIONS_PAN, r.flReflectionsPan); - alEffectf(mEffect, AL_EAXREVERB_LATE_REVERB_GAIN, r.flLateReverbGain); - alEffectf(mEffect, AL_EAXREVERB_LATE_REVERB_DELAY, r.flLateReverbDelay); - alEffectfv(mEffect, AL_EAXREVERB_LATE_REVERB_PAN, r.flLateReverbPan); + alEffectf(mEffect, AL_EAXREVERB_LATE_REVERB_GAIN, r.flLateReverbGain); + alEffectf(mEffect, AL_EAXREVERB_LATE_REVERB_DELAY, r.flLateReverbDelay); + alEffectfv(mEffect, AL_EAXREVERB_LATE_REVERB_PAN, r.flLateReverbPan); - alEffectf(mEffect, AL_EAXREVERB_ECHO_TIME, r.flEchoTime); - alEffectf(mEffect, AL_EAXREVERB_ECHO_DEPTH, r.flEchoDepth); - alEffectf(mEffect, AL_EAXREVERB_MODULATION_TIME, r.flModulationTime); - alEffectf(mEffect, AL_EAXREVERB_MODULATION_DEPTH, r.flModulationDepth); + alEffectf(mEffect, AL_EAXREVERB_ECHO_TIME, r.flEchoTime); + alEffectf(mEffect, AL_EAXREVERB_ECHO_DEPTH, r.flEchoDepth); + alEffectf(mEffect, AL_EAXREVERB_MODULATION_TIME, r.flModulationTime); + alEffectf(mEffect, AL_EAXREVERB_MODULATION_DEPTH, r.flModulationDepth); - alEffectf(mEffect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, r.flAirAbsorptionGainHF); - alEffectf(mEffect, AL_EAXREVERB_HFREFERENCE, r.flHFReference); - alEffectf(mEffect, AL_EAXREVERB_LFREFERENCE, r.flLFReference); - alEffectf(mEffect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, r.flRoomRolloffFactor); - alEffecti(mEffect, AL_EAXREVERB_DECAY_HFLIMIT, r.iDecayHFLimit); + alEffectf(mEffect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, r.flAirAbsorptionGainHF); + alEffectf(mEffect, AL_EAXREVERB_HFREFERENCE, r.flHFReference); + alEffectf(mEffect, AL_EAXREVERB_LFREFERENCE, r.flLFReference); + alEffectf(mEffect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, r.flRoomRolloffFactor); + alEffecti(mEffect, AL_EAXREVERB_DECAY_HFLIMIT, r.iDecayHFLimit); + } } else { + // Standard reverb — subset of EAX, no panning/LF/modulation params alEffecti(mEffect, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - - alEffectf(mEffect, AL_REVERB_DENSITY, r.flDensity); - alEffectf(mEffect, AL_REVERB_DIFFUSION, r.flDiffusion); - alEffectf(mEffect, AL_REVERB_GAIN, r.flGain); - alEffectf(mEffect, AL_REVERB_GAINHF, r.flGainHF); - alEffectf(mEffect, AL_REVERB_DECAY_TIME, r.flDecayTime); - alEffectf(mEffect, AL_REVERB_DECAY_HFRATIO, r.flDecayHFRatio); - alEffectf(mEffect, AL_REVERB_REFLECTIONS_GAIN, r.flReflectionsGain); - alEffectf(mEffect, AL_REVERB_REFLECTIONS_DELAY, r.flReflectionsDelay); - alEffectf(mEffect, AL_REVERB_LATE_REVERB_GAIN, r.flLateReverbGain); - alEffectf(mEffect, AL_REVERB_LATE_REVERB_DELAY, r.flLateReverbDelay); - alEffectf(mEffect, AL_REVERB_AIR_ABSORPTION_GAINHF, r.flAirAbsorptionGainHF); - alEffectf(mEffect, AL_REVERB_ROOM_ROLLOFF_FACTOR, r.flRoomRolloffFactor); - alEffecti(mEffect, AL_REVERB_DECAY_HFLIMIT, r.iDecayHFLimit); + if (alGetError() == AL_NO_ERROR) + { + alEffectf(mEffect, AL_REVERB_DENSITY, r.flDensity); + alEffectf(mEffect, AL_REVERB_DIFFUSION, r.flDiffusion); + alEffectf(mEffect, AL_REVERB_GAIN, r.flGain); + alEffectf(mEffect, AL_REVERB_GAINHF, r.flGainHF); + alEffectf(mEffect, AL_REVERB_DECAY_TIME, r.flDecayTime); + alEffectf(mEffect, AL_REVERB_DECAY_HFRATIO, r.flDecayHFRatio); + alEffectf(mEffect, AL_REVERB_REFLECTIONS_GAIN, r.flReflectionsGain); + alEffectf(mEffect, AL_REVERB_REFLECTIONS_DELAY, r.flReflectionsDelay); + alEffectf(mEffect, AL_REVERB_LATE_REVERB_GAIN, r.flLateReverbGain); + alEffectf(mEffect, AL_REVERB_LATE_REVERB_DELAY, r.flLateReverbDelay); + alEffectf(mEffect, AL_REVERB_AIR_ABSORPTION_GAINHF, r.flAirAbsorptionGainHF); + alEffectf(mEffect, AL_REVERB_ROOM_ROLLOFF_FACTOR, r.flRoomRolloffFactor); + alEffecti(mEffect, AL_REVERB_DECAY_HFLIMIT, r.iDecayHFLimit); + } } - err = alGetError(); - if (err != AL_NO_ERROR) + if (alGetError() == AL_NO_ERROR) + alAuxiliaryEffectSloti(mAuxSlot, AL_EFFECTSLOT_EFFECT, (ALint)mEffect); + else { + Con::warnf("SFXALDevice::setReverb - failed applying reverb parameters"); if (alIsEffect(mEffect)) alDeleteEffects(1, &mEffect); - } - else - { - // Bind updated effect to slot - alAuxiliaryEffectSloti(mAuxSlot, AL_EFFECTSLOT_EFFECT, (ALint)mEffect); + mEffect = 0; } } @@ -547,8 +748,6 @@ void SFXALDevice::setDopplerFactor( F32 factor ) alDopplerFactor( factor ); } -//----------------------------------------------------------------------------- - void SFXALDevice::_setRolloffFactor( F32 factor ) { mRolloffFactor = factor; @@ -573,3 +772,79 @@ void SFXALDevice::setSpeedOfSound(F32 speedOfSound) { alSpeedOfSound(speedOfSound); } + +bool SFXALDevice::isDeviceConnected() +{ + if (!mDevice) + return false; + + // ALC_CONNECTED is a standard-ish extension, check it first + if (alcIsExtensionPresent(mDevice, "ALC_EXT_disconnect") == AL_TRUE) + { + ALCint connected = ALC_FALSE; + alcGetIntegerv(mDevice, ALC_CONNECTED, 1, &connected); + + if (connected == ALC_FALSE) + { + //SFXSystem::removeProvider(mDevice->getProvider()); + } + + return (connected == ALC_TRUE); + } + + // If the extension isn't available, assume connected + return true; +} + +bool SFXALDevice::reconnectDevice(U32 providerIndex) +{ + if (!mDevice || !mContext) + { + Con::errorf("SFXALDevice::reconnectDevice - no active device/context to reconnect"); + return false; + } + + SFXProvider* p = SFXSystem::getProvider(providerIndex); + + // If no new name given, reuse the current device name (i.e. reconnect same device) + if (!p->getName() || p->getName()[0] == '\0') + { + Con::errorf("SFXALDevice::reconnectDevice - unsupported provider given."); + return false; + } + + Con::printf("SFXALDevice::reconnectDevice - reconnecting to '%s'", p->getName()); + + // Fast path: use ALC_SOFT_reopen_device to swap device without losing context. + // All sources/buffers remain valid, playback resumes automatically. + if (mHasSoftReopen && alcReopenDeviceSOFT) + { + // Build attribute list matching current device settings + ALCint attribs[] = + { + ALC_FREQUENCY, smDeviceFrequency, + ALC_MONO_SOURCES, mMaxBuffers, + 0 + }; + + if (alcReopenDeviceSOFT(mDevice, p->getName(), attribs) == ALC_TRUE) + { + Con::printf("SFXALDevice::reconnectDevice - soft reopen succeeded"); + return true; + } + + Con::warnf("SFXALDevice::reconnectDevice - soft reopen failed (error %d), " + "falling back to full recreate", alcGetError(mDevice)); + } + + // Slow path: destroy context and reopen device from scratch. + // SFXSystem::createDevice will handle transitioning sounds to virtual playback. + Con::printf("SFXALDevice::reconnectDevice - performing full device recreate"); + + alcMakeContextCurrent(NULL); + alcDestroyContext(mContext); + mContext = NULL; + alcCloseDevice(mDevice); + mDevice = NULL; + return false; +} diff --git a/Engine/source/sfx/openal/sfxALDevice.h b/Engine/source/sfx/openal/sfxALDevice.h index 7de1e95e1..5ccf3e270 100644 --- a/Engine/source/sfx/openal/sfxALDevice.h +++ b/Engine/source/sfx/openal/sfxALDevice.h @@ -49,58 +49,64 @@ class SFXALDevice : public SFXDevice { - public: +private: + void _initExtensions(); + void _initEFX(); + void _probeEFXEffectCaps(); +public: - typedef SFXDevice Parent; - friend class SFXALVoice; // mDistanceFactor, mRolloffFactor + typedef SFXDevice Parent; + friend class SFXALVoice; // mDistanceFactor, mRolloffFactor - static SFXDevice* createInstance(U32 adapterIndex); + static SFXDevice* createInstance(U32 adapterIndex); - void printALInfo(ALCdevice* device); - void printHRTFInfo(ALCdevice* device); - void getEFXInfo(ALCdevice* device); - S32 getMaxSources(); + void printALInfo(ALCdevice* device); + S32 getMaxSources(); - // Compatibility with pre openal 1.2 - S32 getMaxSourcesOld(); + // Compatibility with pre openal 1.2 + S32 getMaxSourcesOld(); - SFXALDevice(U32 providerIndex); + SFXALDevice(U32 providerIndex); - virtual ~SFXALDevice(); + virtual ~SFXALDevice(); - protected: - static SFXProvider::CreateProviderInstanceDelegate mCreateDeviceInstance; - OPENALFNTABLE mOpenAL; +protected: + static SFXProvider::CreateProviderInstanceDelegate mCreateDeviceInstance; + OPENALFNTABLE mOpenAL; - ALCcontext *mContext; + ALCcontext *mContext; - ALCdevice *mDevice; + ALCdevice *mDevice; - SFXDistanceModel mDistanceModel; - F32 mDistanceFactor; - F32 mRolloffFactor; - F32 mUserRolloffFactor; + SFXDistanceModel mDistanceModel; + F32 mDistanceFactor; + F32 mRolloffFactor; + F32 mUserRolloffFactor; - ALuint mEffect; - ALuint mAuxSlot; - bool mHasEFX; - - void _setRolloffFactor( F32 factor ); + ALuint mEffect; + ALuint mAuxSlot; + bool mHasEFX; + bool mHasSoftHRTF; + bool mHasSoftReopen; - public: - static void enumerateProviders(Vector& providerList); - // SFXDevice. - SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) override; - SFXVoice* createVoice( bool is3D, SFXBuffer *buffer ) override; - void setListener( U32 index, const SFXListenerProperties& listener ) override; - void setDistanceModel( SFXDistanceModel model ) override; - void setDopplerFactor( F32 factor ) override; - void setReverb(const SFXReverbProperties& reverb) override; - void setRolloffFactor( F32 factor ) override; - void resetReverb() override {} - void setSpeedOfSound(F32 speedOfSound) override; + void _setRolloffFactor( F32 factor ); - ALuint getDeviceAuxSlot() { return mAuxSlot; } +public: + static void enumerateProviders(Vector& providerList); + // SFXDevice. + SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) override; + SFXVoice* createVoice( bool is3D, SFXBuffer *buffer ) override; + void setListener( U32 index, const SFXListenerProperties& listener ) override; + void setDistanceModel( SFXDistanceModel model ) override; + void setDopplerFactor( F32 factor ) override; + void setReverb(const SFXReverbProperties& reverb) override; + void setRolloffFactor( F32 factor ) override; + void resetReverb() override {} + void setSpeedOfSound(F32 speedOfSound) override; + bool isDeviceConnected() override; + bool reconnectDevice(U32 providerIndex) override; + + ALuint getDeviceAuxSlot() { return mAuxSlot; } }; #endif // _SFXALDEVICE_H_ diff --git a/Engine/source/sfx/sfxDevice.cpp b/Engine/source/sfx/sfxDevice.cpp index 5c08eeb2e..e800ca420 100644 --- a/Engine/source/sfx/sfxDevice.cpp +++ b/Engine/source/sfx/sfxDevice.cpp @@ -31,6 +31,8 @@ S32 SFXDevice::smUpdateInterval = SFXInternal::DEFAULT_UPDATE_INTERVAL; S32 SFXDevice::smDeviceFrequency = 44100; +S32 SFXDevice::smMaxSendsPerSource = 4; +S32 SFXDevice::smMaxEffectSlots = 4; S8 SFXDevice::smDeviceBitrate = 16; bool SFXDevice::smDeviceHRTF = false; @@ -52,12 +54,23 @@ void SFXDevice::initConsole() Con::addVariable("$pref::SFX::updateInterval", TypeS32, &smUpdateInterval, "The update interval.\n" "@ingroup SFX\n"); + + Con::addVariable("$pref::SFX::maxSendsPerSource", TypeS32, &smMaxSendsPerSource, + "The maximum number sends allowed per source.\n" + "@ingroup SFX\n"); + + Con::addVariable("$pref::SFX::maxEffectSlots", TypeS32, &smMaxEffectSlots, + "The maximum number of effect slots supported by this device.\n" + "@ingroup SFX\n"); } SFXDevice::SFXDevice() : mStatNumBuffers( 0 ), mStatNumVoices( 0 ), - mStatNumBufferBytes( 0 ) + mStatNumBufferBytes( 0 ), + mMaxBuffers(16), + mUseHardware(false), + mCaps(0) { VECTOR_SET_ASSOCIATION( mBuffers ); VECTOR_SET_ASSOCIATION( mVoices ); diff --git a/Engine/source/sfx/sfxDevice.h b/Engine/source/sfx/sfxDevice.h index aa63db7fb..5a7170e31 100644 --- a/Engine/source/sfx/sfxDevice.h +++ b/Engine/source/sfx/sfxDevice.h @@ -55,13 +55,25 @@ class SFXDevice enum ECaps { CAPS_Reverb = BIT(0), ///< Device supports reverb environments. - CAPS_VoiceManagement = BIT(1), ///< Device manages voices on its own; deactivates virtualization code in SFX system. - CAPS_Occlusion = BIT(2), ///< Device has its own sound occlusion handling (SFXOcclusionManager). - CAPS_DSPEffects = BIT(3), ///< Device implements DSP effects (SFXDSPManager). - CAPS_MultiListener = BIT(4), ///< Device supports multiple listeners. - CAPS_HRTF = BIT(5), ///< Device supports HRTF (3D audio positioning). - CAPS_Float32 = BIT(6), ///< Device supports 32-bit float playback. - CAPS_MonoStereo = BIT(7), ///< Device supports mono/stereo output modes. + CAPS_EXTReverb = BIT(1), ///< Device supports extended reverb environments. + CAPS_VoiceManagement = BIT(2), ///< Device manages voices on its own; deactivates virtualization code in SFX system. + CAPS_Occlusion = BIT(3), ///< Device has its own sound occlusion handling (SFXOcclusionManager). + CAPS_DSPEffects = BIT(4), ///< Device implements DSP effects (SFXDSPManager). + CAPS_MultiListener = BIT(5), ///< Device supports multiple listeners. + CAPS_HRTF = BIT(6), ///< Device supports HRTF (3D audio positioning). + CAPS_Float32 = BIT(7), ///< Device supports 32-bit float playback. + CAPS_MonoStereo = BIT(8), ///< Device supports mono/stereo output modes. + CAPS_EFX_EQ = BIT(9), ///< Device Supports Equalizer effect + CAPS_EFX_Compressor = BIT(10), ///< Device Supports Compressor effect + CAPS_EFX_Echo = BIT(11), ///< Device Supports Echo effect + CAPS_EFX_Chorus = BIT(12), ///< Device Supports Chorus effect + CAPS_EFX_Distortion = BIT(13), ///< Device Supports Distortion effect + CAPS_EFX_Flanger = BIT(14), ///< Device Supports Flanger effect + CAPS_SourceFilters = BIT(15), ///< Device Supports Per-source send filters (low/high/band pass) + CAPS_SourceSpatialize = BIT(16), ///< Device Supports Explicit per-source spatialize control + CAPS_HotReconnect = BIT(17), ///< Device Supports Device can be reopened without destroying context + CAPS_DeviceClock = BIT(18), ///< Device Supports High precision device clock + CAPS_DisconnectDetect = BIT(19), ///< Device Can detect hardware disconnection }; static void initConsole(); @@ -73,7 +85,10 @@ class SFXDevice static S8 smDeviceBitrate; /// Does the device use hrtf static bool smDeviceHRTF; - + /// How many effect slots does this device support. + static S32 smMaxEffectSlots; + /// How many sends can each source have. + static S32 smMaxSendsPerSource; protected: typedef Vector< SFXBuffer* > BufferVector; @@ -83,7 +98,7 @@ class SFXDevice typedef VoiceVector::iterator VoiceIterator; /// The provider which created this device. - SFXProvider mProvider; + SFXProvider* mProvider; /// Should the device try to use hardware processing. bool mUseHardware; @@ -133,8 +148,8 @@ public: virtual ~SFXDevice(); /// Returns the provider which created this device. - virtual const SFXProvider& getProvider() { return mProvider; } - virtual void setProvider(const SFXProvider& provider) { mProvider = provider; } + virtual SFXProvider* getProvider() { return mProvider; } + virtual void setProvider(SFXProvider* provider) { mProvider = provider; } /// Is the device set to use hardware processing. bool getUseHardware() const { return mUseHardware; } @@ -143,7 +158,7 @@ public: S32 getMaxBuffers() const { return mMaxBuffers; } /// Returns the name of this device. - const String& getName() const { return mProvider.getName(); } + const String& getName() const { return mProvider->getName(); } /// Return the device capability flags. U32 getCaps() const { return mCaps; } @@ -191,6 +206,9 @@ public: /// Set the global reverb environment. virtual void setReverb( const SFXReverbProperties& reverb ) {} + + virtual bool isDeviceConnected() { return true; } + virtual bool reconnectDevice(U32 provider) { return false; } /// Reset the global reverb environment to its default. virtual void resetReverb() {} diff --git a/Engine/source/sfx/sfxSystem.cpp b/Engine/source/sfx/sfxSystem.cpp index a31fa8304..8ae533fab 100644 --- a/Engine/source/sfx/sfxSystem.cpp +++ b/Engine/source/sfx/sfxSystem.cpp @@ -305,60 +305,60 @@ SFXProvider* SFXSystem::getProviderByTypeAndName(SFXProviderType type, const cha SFXProvider* SFXSystem::getBestProviderChoice() { - const String provider = Con::getVariable("$pref::SFX::provider"); - const String device = Con::getVariable("$pref::SFX::device"); + const String preferredProvider = Con::getVariable("$pref::SFX::provider"); + const String preferredDevice = Con::getVariable("$pref::SFX::device"); + // Resolve provider type. SFXProviderType providerType = OpenAL; - if (provider.isEmpty() || device.isEmpty()) + if (!preferredProvider.isEmpty()) { - for (U32 i = 0; i < smProviders.size(); i++) - { - if (smProviders[i]->mType == OpenAL) - { - if (smProviders[i]->mDefault) - return smProviders[i]; - } - } - } - else - { - S32 ret = -1; for (U32 i = 0; i < SFXProviderType_Count; i++) { - if (!dStrcmp(getProviderNameFromType((SFXProviderType)i), provider)) + if (!dStrcmp(getProviderNameFromType((SFXProviderType)i),preferredProvider)) { - ret = i; + providerType = (SFXProviderType)i; break; } } - - if (ret == -1) - providerType = OpenAL; } - U32 i = 0; - for (i = 0; i < smProviders.size(); i++) + if (!preferredDevice.isEmpty()) { - if (smProviders[i]->mType == providerType) + for (U32 i = 0; i < smProviders.size(); i++) { - if (String::compare(smProviders[i]->getName(), device.c_str()) == 0) + SFXProvider* provider = smProviders[i]; + + if (provider->mType != providerType) + continue; + + if (String::compare(provider->getName(), preferredDevice.c_str()) == 0) { - return smProviders[i]; + return provider; } } } - for (i = 0; i < smProviders.size(); i++) + for (U32 i = 0; i < smProviders.size(); i++) { - if (smProviders[i]->mType == providerType) - { - if (smProviders[i]->mDefault) - return smProviders[i]; - } + SFXProvider* provider = smProviders[i]; + + if (provider->mType != providerType) + continue; + + if (provider->mDefault) + return provider; } - + for (U32 i = 0; i < smProviders.size(); i++) + { + SFXProvider* provider = smProviders[i]; + + if (provider->mType != providerType) + continue; + + return provider; + } return NULL; } @@ -649,28 +649,11 @@ bool SFXSystem::createDevice(SFXProvider* provider) Con::errorf( "SFXSystem::createDevice - failed creating device '%s'", provider->getName()); return false; } - mDevice->setProvider(*provider); - // Print capabilities. - Con::printf( "\nSFXSystem::createDevice - created device '%s'", provider->getName()); - Con::printf("| Device Update Interval: %d ms", SFXDevice::smUpdateInterval); - Con::printf("| Device Sample rate: %d Hz", SFXDevice::smDeviceFrequency); - Con::printf("| Device Bitrate: %d", SFXDevice::smDeviceBitrate); - if (mDevice->getCaps() & SFXDevice::CAPS_Reverb) - Con::printf("| CAPS_Reverb"); - if (mDevice->getCaps() & SFXDevice::CAPS_VoiceManagement) - Con::printf("| CAPS_VoiceManagement"); - if (mDevice->getCaps() & SFXDevice::CAPS_Occlusion) - Con::printf("| CAPS_Occlusion"); - if (mDevice->getCaps() & SFXDevice::CAPS_MultiListener) - Con::printf("| CAPS_MultiListener"); - if (mDevice->getCaps() & SFXDevice::CAPS_HRTF) - Con::printf("| CAPS_HRTF"); - if (mDevice->getCaps() & SFXDevice::CAPS_Float32) - Con::printf("| CAPS_Float32"); - if (mDevice->getCaps() & SFXDevice::CAPS_MonoStereo) - Con::printf("| CAPS_MonoStereo"); - + mDevice->setProvider(provider); + + // device is responsible for printing its information. + // Set defaults. mDevice->setNumListeners( getNumListeners() ); mDevice->setDistanceModel( mDistanceModel ); @@ -696,8 +679,8 @@ String SFXSystem::getDeviceInfoString() return String(); return String::ToString( "%s\t%s\t%s\t%d\t%d", - getProviderNameFromType(mDevice->getProvider().mType), - mDevice->getProvider().getName(), + getProviderNameFromType(mDevice->getProvider()->mType), + mDevice->getProvider()->getName(), mDevice->getUseHardware() ? "1" : "0", mDevice->getMaxBuffers(), mDevice->getCaps() ); @@ -1017,8 +1000,10 @@ void SFXSystem::_update() } // If we have a device then update it. - if( mDevice ) + if (mDevice) + { mDevice->update(); + } } //-----------------------------------------------------------------------------