diff --git a/Engine/source/sfx/sfxDevice.h b/Engine/source/sfx/sfxDevice.h index 45d1df766..aa63db7fb 100644 --- a/Engine/source/sfx/sfxDevice.h +++ b/Engine/source/sfx/sfxDevice.h @@ -82,9 +82,6 @@ class SFXDevice typedef BufferVector::iterator BufferIterator; typedef VoiceVector::iterator VoiceIterator; - /// The name of this device. - String mName; - /// The provider which created this device. SFXProvider mProvider; @@ -146,7 +143,7 @@ public: S32 getMaxBuffers() const { return mMaxBuffers; } /// Returns the name of this device. - const String& getName() const { return mName; } + const String& getName() const { return mProvider.getName(); } /// Return the device capability flags. U32 getCaps() const { return mCaps; } diff --git a/Engine/source/sfx/sfxSystem.cpp b/Engine/source/sfx/sfxSystem.cpp index 380173237..a31fa8304 100644 --- a/Engine/source/sfx/sfxSystem.cpp +++ b/Engine/source/sfx/sfxSystem.cpp @@ -263,6 +263,46 @@ const char* SFXSystem::getProviderNameFromType(SFXProviderType type) return _names[type]; } +SFXProviderType SFXSystem::getProviderTypeFromName(const char* name) +{ + for (U32 i = 0; i < SFXProviderType_Count; i++) + { + if (dStrcmp(getProviderNameFromType((SFXProviderType)i), name) == 0) + return (SFXProviderType)i; + } + + // Default to NullProvider if no match rather than silently falling through + Con::warnf("SFXSystem::getProviderTypeFromName - unknown provider '%s', defaulting to NullProvider", name); + return SFXProviderType::NullProvider; +} + +SFXProvider* SFXSystem::getProviderByTypeAndName(SFXProviderType type, const char* deviceName) +{ + // First try exact type+name match + for (U32 i = 0; i < smProviders.size(); i++) + { + if (smProviders[i]->mType == type && + String::compare(smProviders[i]->getName(), deviceName) == 0) + return smProviders[i]; + } + + // Fall back to default device for that type + for (U32 i = 0; i < smProviders.size(); i++) + { + if (smProviders[i]->mType == type && smProviders[i]->mDefault) + return smProviders[i]; + } + + // Last resort: any device of that type + for (U32 i = 0; i < smProviders.size(); i++) + { + if (smProviders[i]->mType == type) + return smProviders[i]; + } + + return NULL; +} + SFXProvider* SFXSystem::getBestProviderChoice() { const String provider = Con::getVariable("$pref::SFX::provider"); @@ -572,13 +612,36 @@ void SFXSystem::removePlugin( SFXSystemPlugin* plugin ) } } +bool SFXSystem::createDeviceByName(const char* providerName, const char* deviceName) +{ + SFXProviderType type = getProviderTypeFromName(providerName); + SFXProvider* provider = getProviderByTypeAndName(type, deviceName); + + if (!provider) + { + Con::errorf("SFXSystem::createDeviceByName - could not find provider '%s' device '%s'", + providerName, deviceName); + return false; + } + + return createDevice(provider); +} + //----------------------------------------------------------------------------- bool SFXSystem::createDevice(SFXProvider* provider) { // this should probably just happen tbh. if (mDevice) + { + // called to create the same device bug out. + // this may need to be changed later to check if + // the provider names also match. + if (String::compare(mDevice->getName(), provider->getName()) == 0) + return false; + deleteDevice(); + } mDevice = provider->mCreateDeviceInstanceDelegate(provider->mIndex); if( !mDevice ) @@ -613,7 +676,7 @@ bool SFXSystem::createDevice(SFXProvider* provider) mDevice->setDistanceModel( mDistanceModel ); mDevice->setDopplerFactor( mDopplerFactor ); mDevice->setRolloffFactor( mRolloffFactor ); - mDevice->setSpeedOfSound(mSpeedOfSound); + mDevice->setSpeedOfSound( mSpeedOfSound ); //OpenAL requires slots for effects, this creates an empty function //that will run when a sfxdevice is created. mDevice->openSlots(); @@ -1492,6 +1555,26 @@ DefineEngineFunction( sfxCreateDevice, bool, (),, //----------------------------------------------------------------------------- +DefineEngineFunction(sfxCreateDeviceFromName, bool, (const char* providerName, const char* deviceName), ("", ""), + "Create a sound device. Optionally specify provider and device name directly.\n" + "@param providerName Optional provider type name (e.g. \"OpenAL\", \"NullProvider\").\n" + "@param deviceName Optional device name to match against.\n" + "@ingroup SFX") +{ + // If explicit names were provided, use them directly + if (providerName[0] != '\0' && deviceName[0] != '\0') + return SFX->createDeviceByName(providerName, deviceName); + + // Otherwise fall back to autodetect via prefs + SFXProvider* p = SFXSystem::getBestProviderChoice(); + if (p) + return SFX->createDevice(p); + + return false; +} + +//----------------------------------------------------------------------------- + DefineEngineFunction( sfxDeleteDevice, void, (),, "Delete the currently active sound device and release all its resources.\n" "SFXSources that are still playing will be transitioned to virtualized playback mode. " diff --git a/Engine/source/sfx/sfxSystem.h b/Engine/source/sfx/sfxSystem.h index b1157574e..a54f4be20 100644 --- a/Engine/source/sfx/sfxSystem.h +++ b/Engine/source/sfx/sfxSystem.h @@ -115,6 +115,8 @@ public: static const char* getProviderNameFromType(SFXProviderType type); static SFXProvider* getBestProviderChoice(); static SFXProvider* getProvider(U32 index); + static SFXProviderType getProviderTypeFromName(const char* name); + static SFXProvider* getProviderByTypeAndName(SFXProviderType type, const char* deviceName); typedef Signal< void( SFXSystemEventType event ) > EventSignalType; typedef Vector< SFXSource* > SFXSourceVector; @@ -274,10 +276,12 @@ public: /// Unregister the given plugin with the system. void removePlugin( SFXSystemPlugin* plugin ); - + /// @name Device Management /// @{ + bool createDeviceByName(const char* providerName, const char* deviceName); + /// This initializes a new device. /// /// @return Returns true if the device was created. diff --git a/Templates/BaseGame/game/core/gui/scripts/canvas.tscript b/Templates/BaseGame/game/core/gui/scripts/canvas.tscript index 8d6a0d98b..cddee4ecb 100644 --- a/Templates/BaseGame/game/core/gui/scripts/canvas.tscript +++ b/Templates/BaseGame/game/core/gui/scripts/canvas.tscript @@ -25,6 +25,7 @@ function createCanvas(%windowTitle) if ($isDedicated) { GFXInit::createNullDevice(); + sfxCreateDevice("Null","Null Device", false, 8); return true; } diff --git a/Templates/BaseGame/game/core/sfx/Core_SFX.tscript b/Templates/BaseGame/game/core/sfx/Core_SFX.tscript index 81a6b952b..65eb5ef39 100644 --- a/Templates/BaseGame/game/core/sfx/Core_SFX.tscript +++ b/Templates/BaseGame/game/core/sfx/Core_SFX.tscript @@ -12,4 +12,12 @@ function Core_SFX::onCreate(%this) function Core_SFX::onDestroy(%this) { +} + +function Core_SFX::initClient(%this) +{ + if($pref::SFX::autoDetect) + { + AutodetectSound(); + } } \ No newline at end of file diff --git a/Templates/BaseGame/game/core/sfx/scripts/audioOptions.tscript b/Templates/BaseGame/game/core/sfx/scripts/audioOptions.tscript index 8ac4da6c1..94aed17fa 100644 --- a/Templates/BaseGame/game/core/sfx/scripts/audioOptions.tscript +++ b/Templates/BaseGame/game/core/sfx/scripts/audioOptions.tscript @@ -26,55 +26,92 @@ new SimGroup(AudioSettingsGroup) }; }; +function getDeviceDisplayName(%fullName) +{ + %openPos = strstr(%fullName, "("); + %closePos = strstr(%fullName, ")"); + + if (%openPos == -1 || %closePos == -1) + return %fullName; + + // +1 to skip past the "(" itself + return trim(getSubStr(%fullName, %openPos + 1, %closePos - %openPos - 1)); +} + function AudioSettingsGroup::populateSettings(%this) { AudioSettingsProviderGroup.clear(); AudioSettingsDeviceGroup.clear(); %pCount = sfxGetProviderCount(); - for(%i = 0; %i < %pCount; %i++) + + // --- First pass: build deduplicated provider list --- + for (%i = 0; %i < %pCount; %i++) { - %provider = sfxGetProviderType(%i); - if(%provider !$= "NullProvider") + %providerType = sfxGetProviderType(%i); + + if (%providerType $= "NullProvider") + continue; + + %alreadyAdded = false; + foreach (%entry in AudioSettingsProviderGroup) { - //We can't have duplicate providers, so double check for uniqueness - %foundProvider = false; - foreach(%registeredProviders in AudioSettingsProviderGroup) + if (%entry.displayName $= %providerType) { - if(%registeredProviders.displayName $= %provider) - { - %foundProvider = true; - break; - } - } - - if(!%foundProvider) - { - //Provider entry - %providerEntry = new ArrayObject() - { - class = "OptionsQualityLevel"; - displayName = %provider; - key["$pref::SFX::provider"] = %provider; - }; - AudioSettingsProviderGroup.add(%providerEntry); + %alreadyAdded = true; + break; } } - } - - for(%i = 0; %i < %pCount; %i++) - { - %device = sfxGetProviderDevice(%i); - //Device Entry - %deviceEntry = new ArrayObject() - { - class = "OptionsQualityLevel"; - displayName = %device; - provider = %provider; //this is for filtering later, if we need to - key["$pref::SFX::device"] = %device; - }; - AudioSettingsDeviceGroup.add(%deviceEntry); + if (!%alreadyAdded) + { + %providerEntry = new ArrayObject() + { + class = "OptionsQualityLevel"; + displayName = %providerType; + key["$pref::SFX::provider"] = %providerType; + }; + AudioSettingsProviderGroup.add(%providerEntry); + } + } + + // --- Second pass: for each provider, collect its devices --- + foreach (%providerEntry in AudioSettingsProviderGroup) + { + %providerType = %providerEntry.displayName; + + for (%i = 0; %i < %pCount; %i++) + { + if (sfxGetProviderType(%i) !$= %providerType) + continue; + + %deviceName = sfxGetProviderDevice(%i); + + %deviceEntry = new ArrayObject() + { + class = "OptionsQualityLevel"; + displayName = getDeviceDisplayName(%deviceName); + providerType = %providerType; + key["$pref::SFX::device"] = %deviceName; + }; + AudioSettingsDeviceGroup.add(%deviceEntry); + } + } + + // Highlight the currently active entries + %activeProvider = $pref::SFX::provider; + %activeDevice = $pref::SFX::device; + + foreach (%entry in AudioSettingsProviderGroup) + { + if (%entry.displayName $= %activeProvider) + AudioSettingsProviderGroup.currentValue = %activeProvider; + } + + foreach (%entry in AudioSettingsDeviceGroup) + { + if (%entry.displayName $= %activeDevice && %entry.providerType $= %activeProvider) + AudioSettingsDeviceGroup.currentValue = %activeDevice; } } @@ -90,8 +127,15 @@ function AudioSettingsDeviceGroup::onApply(%this) function updateAudioOptionsSettings() { - if ( !sfxCreateDevice() ) + if ( !sfxCreateDeviceFromName($pref::SFX::provider, $pref::SFX::device) ) error( "Unable to create SFX device: " @ $pref::SFX::provider SPC $pref::SFX::device SPC $pref::SFX::useHardware ); +} + +function AutodetectSound() +{ + // Nothing to actually do in here? + // function here for convenience. + $pref::SFX::autoDetect = false; } \ No newline at end of file diff --git a/Templates/BaseGame/game/data/UI/guis/optionsMenu.tscript b/Templates/BaseGame/game/data/UI/guis/optionsMenu.tscript index 49fe38a91..d3ab3dca8 100644 --- a/Templates/BaseGame/game/data/UI/guis/optionsMenu.tscript +++ b/Templates/BaseGame/game/data/UI/guis/optionsMenu.tscript @@ -855,6 +855,8 @@ function OptionsMenu::resetSettings(%this) function OptionsMenu::populateAudioSettings(%this) { AudioSettingsList.clear(); + OptionsMenu.optsListCount = -1; + AudioSettingsGroup.populateSettings(); //Process the lists