What I’m trying to do:
I am trying to switch the current audio output device through the new Audio Engine on the fly and at runtime. From looking around the forums and the AnwserHub it’s clear that I’m not the only person trying and failing to do this; but I think I’m super close and I’m hoping someone here can help push this over the line
This is being done in UE4.25.4
What’s been done so far:
I’m currently able to get the current active device, as well as a list of available devices. The only thing that isn’t working is changing the current device.
How I’m attempting this:
I’m currently creating the functions and storing the variables in the game’s instance.
In the instance header I’m storing a pointer to the game’s Audio Mixer Platform Interface, and I am acquiring this during the Init() function. The cast from FAudioDevice to FMixerDevice works perfectly fine and GetAudioMixerPlatform() function returns the game’s MixerPlatformInterface (I “borrowed” this from an existing function in the UE4 as I was struggling to figure out how to get hold of the MixerPlatformInterface).
// In the Instance.h
Audio::IAudioMixerPlatformInterface *AudioMixerPlatform;
// In the Instance.cpp
void UTestInstance::Init()
{ Audio::FMixerDevice *MainMixer = static_cast<Audio::FMixerDevice*>( GetWorld()->GetAudioDeviceRaw() );
AudioMixerPlatform = MainMixer->GetAudioMixerPlatform();
}
I have a simple blueprint exposed function to get the current device. I’m using the GetPlatformDeviceInfo() function because the GetCurrentDeviceName() is always blank.
// In the Instance.cpp
void UTestInstance::Audio_CurrentDevice_Get( FString &DeviceName )
{ DeviceName = AudioMixerPlatform->GetPlatformDeviceInfo().Name;
}
To get a list of available devices I’m using the GetAudioDeviceList() function which can be called from an active device handle because it returns a neat array of FStrings. I’m wrapping this in my own function so that it can be exposed to blueprint.
// In the instance.cpp
void UTestInstance::Audio_Devices_Get( TArray<FString> &DeviceNames )
{ FAudioDeviceHandle MainDeviceHandle = GEngine->GetActiveAudioDevice();
MainDeviceHandle.GetAudioDevice()->GetAudioDeviceList( DeviceNames );
}
Finally I have a function to return a list of devices as FAudioPlatformInfo structs, this is because these structs have both the device name and the device ID and we can use these to find a device ID by the devices name. This function is not exposed to blueprint because FAudioPlatformDeiceInfo is not blueprintable, plus there is no real need to.
// In the instance.cpp
void UTestInstance::Audio_DevicesAsPlatformInfo_Get( TArray<Audio::FAudioPlatformDeviceInfo> &PlatformDevices )
{ // Get the number of output devices
uint32 DeviceNum = 0;
AudioMixerPlatform->GetNumOutputDevices( DeviceNum );
// Get all the audio devices are platformDeviceInfo structs
TArray<Audio::FAudioPlatformDeviceInfo> PlatformDevicesArrary;
for( uint32 i = 0; i < DeviceNum; i++ )
{
[INDENT=2]Audio::FAudioPlatformDeviceInfo PlatformDeviceInfo;
AudioMixerPlatform->GetOutputDeviceInfo( i, PlatformDeviceInfo );
PlatformDevicesArrary.Add( PlatformDeviceInfo );[/INDENT]
}
// Output the Platform Devices Info Array
PlatformDevices = PlatformDevicesArrary;
}
All of these functions are working perfectly! They function exactly as wanted.
What’s not working:
My final function is the function to set a new audio device by name, this is again exposed to blueprint for ease of use. For this function we first get an array of all the devices as FAudioPlatformDeviceInfo structs, then we loop through this array and compare the names of each device to the device name that as passed into the function, and finally if we find a matching name then we pass the device’s ID into the IAudioMixerPlatformInterface::MoveAudioStreamToNewAudioDevice() function and grab the returning bool from that function to return from our function…
// In the instance.cpp
bool UTestInstance::Audio_CurrentDevice_Set( const FString &DeviceName )
{ // The return value
bool bReturn = false;
// Get all the audio devices are platformDeviceInfo structs
TArray<Audio::FAudioPlatformDeviceInfo> PlatformDevices;
Audio_DevicesAsPlatformInfo_Get( PlatformDevices );
// Find our device, and set the audio output
for( auto Elem : PlatformDevices )
{
[INDENT=2]if( Elem.Name == DeviceName )
{[/INDENT]
[INDENT=3]bReturn = AudioMixerPlatform->MoveAudioStreamToNewAudioDevice( Elem.DeviceId );[/INDENT]
[INDENT=2]}[/INDENT]
}
// Return success or failure
return bReturn;
}
The function executes perfectly and MoveAudioStreamToNewAudioDevice() returns true, and what’s more after calling this function the Mixer Platform’s current device output is the device we asked to change to, so the system seems to think that it has worked; but the actual sound from the game doesn’t change to a different device it still outputs from the system default.
Other attempts:
I have tried using IAudioMixerPlatformInterface::OpenAudioStream(), after calling CloseAudioStream(), to try to create a brand new stream with the new device, but this failed and then crashed the game.
I tried calling IAudioMixerPlatformInterface::ResumePlaybackOnNewDevice() after a successful call to MoveAudioStreamToNewAudioDevice(), but that changed nothing.
Summary:
Everything seems to working and the AudioMixerPlatform definitely seems to think that it has indeed changed devices, but the actual audio output is still stuck on the system default. I must be missing something, I feel like this is sooo close, I would really appreciate any input or ideas you guys have.
Thanks in advance