Files
UnrealEngine/Engine/Source/Runtime/AudioMixer/Private/AudioBusSubsystem.cpp
2025-05-18 13:04:45 +08:00

295 lines
9.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AudioBusSubsystem.h"
#include "AudioMixerDevice.h"
#include "AudioMixerSourceManager.h"
#include "DSP/MultithreadedPatching.h"
#include "UObject/UObjectIterator.h"
std::atomic<uint32> Audio::FAudioBusKey::InstanceIdCounter = 0;
UAudioBusSubsystem::UAudioBusSubsystem()
{
}
bool UAudioBusSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
return !IsRunningDedicatedServer();
}
void UAudioBusSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
UE_LOG(LogAudioMixer, Log, TEXT("Initializing Audio Bus Subsystem for audio device with ID %d"), GetMixerDevice()->DeviceID);
InitDefaultAudioBuses();
}
void UAudioBusSubsystem::Deinitialize()
{
UE_LOG(LogAudioMixer, Log, TEXT("Deinitializing Audio Bus Subsystem for audio device with ID %d"), GetMixerDevice() ? GetMixerDevice()->DeviceID : -1);
ShutdownDefaultAudioBuses();
}
void UAudioBusSubsystem::StartAudioBus(Audio::FAudioBusKey InAudioBusKey, int32 InNumChannels, bool bInIsAutomatic)
{
StartAudioBus(InAudioBusKey, FString(), InNumChannels, bInIsAutomatic);
}
void UAudioBusSubsystem::StartAudioBus(Audio::FAudioBusKey InAudioBusKey, const FString& InAudioBusName, int32 InNumChannels, bool bInIsAutomatic)
{
if (IsInGameThread())
{
if (ActiveAudioBuses_GameThread.Contains(InAudioBusKey))
{
return;
}
FActiveBusData BusData;
BusData.BusKey = InAudioBusKey;
BusData.NumChannels = InNumChannels;
BusData.bIsAutomatic = bInIsAutomatic;
ActiveAudioBuses_GameThread.Add(InAudioBusKey, BusData);
FAudioThread::RunCommandOnAudioThread([this, InAudioBusKey, InAudioBusName, InNumChannels, bInIsAutomatic]()
{
if (Audio::FMixerSourceManager* MixerSourceManager = GetMutableSourceManager())
{
MixerSourceManager->StartAudioBus(InAudioBusKey, InAudioBusName, InNumChannels, bInIsAutomatic);
}
});
}
else
{
// If we're not the game thread, this needs to be on the game thread, so queue up a command to execute it on the game thread
if (Audio::FMixerDevice* MixerDevice = GetMutableMixerDevice())
{
MixerDevice->GameThreadMPSCCommand([this, InAudioBusKey, InAudioBusName, InNumChannels, bInIsAutomatic]
{
StartAudioBus(InAudioBusKey, InAudioBusName, InNumChannels, bInIsAutomatic);
});
}
}
}
void UAudioBusSubsystem::StopAudioBus(Audio::FAudioBusKey InAudioBusKey)
{
if (IsInGameThread())
{
if (!ActiveAudioBuses_GameThread.Contains(InAudioBusKey))
{
return;
}
ActiveAudioBuses_GameThread.Remove(InAudioBusKey);
FAudioThread::RunCommandOnAudioThread([this, InAudioBusKey]()
{
if (Audio::FMixerSourceManager* MixerSourceManager = GetMutableSourceManager())
{
MixerSourceManager->StopAudioBus(InAudioBusKey);
}
});
}
else
{
// If we're not the game thread, this needs to be on the game thread, so queue up a command to execute it on the game thread
if (Audio::FMixerDevice* MixerDevice = GetMutableMixerDevice())
{
MixerDevice->GameThreadMPSCCommand([this, InAudioBusKey]
{
StopAudioBus(InAudioBusKey);
});
}
}
}
bool UAudioBusSubsystem::IsAudioBusActive(Audio::FAudioBusKey InAudioBusKey) const
{
if (IsInGameThread())
{
return ActiveAudioBuses_GameThread.Contains(InAudioBusKey);
}
check(IsInAudioThread());
if (const Audio::FMixerSourceManager* MixerSourceManager = GetSourceManager())
{
return MixerSourceManager->IsAudioBusActive(InAudioBusKey);
}
return false;
}
Audio::FPatchInput UAudioBusSubsystem::AddPatchInputForAudioBus(Audio::FAudioBusKey InAudioBusKey, int32 InFrames, int32 InChannels, float InGain)
{
Audio::FMixerSourceManager* SourceManager = GetMutableSourceManager();
check(SourceManager);
Audio::FMixerDevice* MixerDevice = GetMutableMixerDevice();
if (!MixerDevice)
{
return Audio::FPatchInput();
}
Audio::FPatchInput PatchInput = MixerDevice->MakePatch(InFrames, InChannels, InGain);
SourceManager->AddPendingAudioBusConnection(InAudioBusKey, InChannels, false, PatchInput);
return PatchInput;
}
Audio::FPatchOutputStrongPtr UAudioBusSubsystem::AddPatchOutputForAudioBus(Audio::FAudioBusKey InAudioBusKey, int32 InFrames, int32 InChannels, float InGain)
{
Audio::FMixerSourceManager* SourceManager = GetMutableSourceManager();
check(SourceManager);
Audio::FMixerDevice* MixerDevice = GetMutableMixerDevice();
if (!MixerDevice)
{
return nullptr;
}
Audio::FPatchOutputStrongPtr PatchOutput = MixerDevice->MakePatch(InFrames, InChannels, InGain);
SourceManager->AddPendingAudioBusConnection(InAudioBusKey, InChannels, false, PatchOutput);
return PatchOutput;
}
Audio::FPatchInput UAudioBusSubsystem::AddPatchInputForSoundAndAudioBus(uint64 SoundInstanceID, Audio::FAudioBusKey AudioBusKey, int32 InFrames, int32 NumChannels, float InGain)
{
Audio::FMixerDevice* MixerDevice = GetMutableMixerDevice();
if (!MixerDevice)
{
return {};
}
if (Audio::FPatchOutputStrongPtr PatchOutput = MixerDevice->MakePatch(InFrames, NumChannels, InGain))
{
Audio::FPatchInput PatchInput = MoveTemp(PatchOutput);
AddPendingConnection(SoundInstanceID, FPendingConnection{ FPendingConnection::FPatchVariant(TInPlaceType<Audio::FPatchInput>(), PatchInput), MoveTemp(AudioBusKey), InFrames, NumChannels });
return PatchInput;
}
return {};
}
Audio::FPatchOutputStrongPtr UAudioBusSubsystem::AddPatchOutputForSoundAndAudioBus(uint64 SoundInstanceID, Audio::FAudioBusKey AudioBusKey, int32 InFrames, int32 NumChannels, float InGain)
{
Audio::FMixerDevice* MixerDevice = GetMutableMixerDevice();
if (!MixerDevice)
{
return {};
}
if (Audio::FPatchOutputStrongPtr PatchOutput = MixerDevice->MakePatch(InFrames, NumChannels, InGain))
{
AddPendingConnection(SoundInstanceID, FPendingConnection{ FPendingConnection::FPatchVariant(TInPlaceType<Audio::FPatchOutputStrongPtr>(), PatchOutput), MoveTemp(AudioBusKey), InFrames, NumChannels });
return PatchOutput;
}
return {};
}
void UAudioBusSubsystem::AddPendingConnection(uint64 SoundInstanceID, FPendingConnection&& PendingConnection)
{
FScopeLock ScopeLock(&Mutex);
FSoundInstanceConnections& SoundInstanceConnections = SoundInstanceConnectionMap.FindOrAdd(SoundInstanceID);
SoundInstanceConnections.PendingConnections.Add(MoveTemp(PendingConnection));
}
void UAudioBusSubsystem::ConnectPatches(uint64 SoundInstanceID)
{
TArray<FPendingConnection> PendingConnections = ExtractPendingConnectionsIfReady(SoundInstanceID);
if (!PendingConnections.IsEmpty())
{
Audio::FMixerSourceManager* SourceManager = GetMutableSourceManager();
check(SourceManager);
for (FPendingConnection& PendingConnection : PendingConnections)
{
switch (PendingConnection.PatchVariant.GetIndex())
{
case FPendingConnection::FPatchVariant::IndexOfType<Audio::FPatchInput>():
SourceManager->AddPendingAudioBusConnection(MoveTemp(PendingConnection.AudioBusKey), PendingConnection.NumChannels, PendingConnection.bIsAutomatic, MoveTemp(PendingConnection.PatchVariant.Get<Audio::FPatchInput>()));
break;
case FPendingConnection::FPatchVariant::IndexOfType<Audio::FPatchOutputStrongPtr>():
SourceManager->AddPendingAudioBusConnection(MoveTemp(PendingConnection.AudioBusKey), PendingConnection.NumChannels, PendingConnection.bIsAutomatic, MoveTemp(PendingConnection.PatchVariant.Get<Audio::FPatchOutputStrongPtr>()));
break;
}
}
}
}
void UAudioBusSubsystem::RemoveSound(uint64 SoundInstanceID)
{
FScopeLock ScopeLock(&Mutex);
SoundInstanceConnectionMap.Remove(SoundInstanceID);
}
TArray<UAudioBusSubsystem::FPendingConnection> UAudioBusSubsystem::ExtractPendingConnectionsIfReady(uint64 SoundInstanceID)
{
FScopeLock ScopeLock(&Mutex);
if (FSoundInstanceConnections* SoundInstanceConnections = SoundInstanceConnectionMap.Find(SoundInstanceID))
{
TArray<FPendingConnection> PendingConnections = MoveTemp(SoundInstanceConnections->PendingConnections);
SoundInstanceConnections->PendingConnections.Empty();
return PendingConnections;
}
return {};
}
void UAudioBusSubsystem::InitDefaultAudioBuses()
{
if (!ensure(IsInGameThread()))
{
return;
}
if (const UAudioSettings* AudioSettings = GetDefault<UAudioSettings>())
{
TArray<TStrongObjectPtr<UAudioBus>> StaleBuses = DefaultAudioBuses;
DefaultAudioBuses.Reset();
for (const FDefaultAudioBusSettings& BusSettings : AudioSettings->DefaultAudioBuses)
{
if (UObject* BusObject = BusSettings.AudioBus.TryLoad())
{
if (UAudioBus* AudioBus = Cast<UAudioBus>(BusObject))
{
const int32 NumChannels = static_cast<int32>(AudioBus->AudioBusChannels) + 1;
StartAudioBus(Audio::FAudioBusKey(AudioBus->GetUniqueID()), AudioBus->GetPathName(), NumChannels, false /* bInIsAutomatic */);
TStrongObjectPtr<UAudioBus>AddedBus(AudioBus);
DefaultAudioBuses.AddUnique(AddedBus);
StaleBuses.Remove(AddedBus);
}
}
}
for (TStrongObjectPtr<UAudioBus>& Bus : StaleBuses)
{
if (Bus.IsValid())
{
StopAudioBus(Audio::FAudioBusKey(Bus->GetUniqueID()));
}
}
}
else
{
UE_LOG(LogAudioMixer, Error, TEXT("Failed to initialize Default Audio Buses. Audio Settings not found."));
}
}
void UAudioBusSubsystem::ShutdownDefaultAudioBuses()
{
if (!ensure(IsInGameThread()))
{
return;
}
for (TObjectIterator<UAudioBus> It; It; ++It)
{
UAudioBus* AudioBus = *It;
if (AudioBus)
{
StopAudioBus(Audio::FAudioBusKey(AudioBus->GetUniqueID()));
}
}
DefaultAudioBuses.Reset();
}