void USoundMod::Parse(class FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances) { FWaveInstance* WaveInstance = ActiveSound.FindWaveInstance(NodeWaveInstanceHash); // Create a new WaveInstance if this SoundWave doesn't already have one associated with it. if (WaveInstance == NULL) { const int32 SampleRate = 44100; // Create a new wave instance and associate with the ActiveSound WaveInstance = new FWaveInstance(&ActiveSound); WaveInstance->WaveInstanceHash = NodeWaveInstanceHash; ActiveSound.WaveInstances.Add(NodeWaveInstanceHash, WaveInstance); // Create streaming wave object USoundModWave* ModWave = NewObject<USoundModWave>(); ModWave->SampleRate = SampleRate; ModWave->NumChannels = 2; ModWave->Duration = INDEFINITELY_LOOPING_DURATION; ModWave->bLooping = bLooping; if (ResourceData == NULL) { RawData.GetCopy((void**)&ResourceData, true); } ModWave->xmpContext = xmp_create_context(); xmp_load_module_from_memory(ModWave->xmpContext, ResourceData, RawData.GetBulkDataSize()); xmp_start_player(ModWave->xmpContext, SampleRate, 0); WaveInstance->WaveData = ModWave; } WaveInstance->WaveData->Parse(AudioDevice, NodeWaveInstanceHash, ActiveSound, ParseParams, WaveInstances); }
/* USoundWave overrides *****************************************************************************/ void URawAudioSoundWave::Parse(class FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances) { Super::Parse(AudioDevice, NodeWaveInstanceHash, ActiveSound, ParseParams, WaveInstances); FWaveInstance* WaveInstance = ActiveSound.FindWaveInstance(NodeWaveInstanceHash); if (WaveInstance) { // Hack //bPassiveMixEnabled = QueuedAudio.Num() > 0; } }
void USoundWave::Parse( FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances ) { FWaveInstance* WaveInstance = ActiveSound.FindWaveInstance(NodeWaveInstanceHash); // Create a new WaveInstance if this SoundWave doesn't already have one associated with it. if( WaveInstance == NULL ) { if( !ActiveSound.bRadioFilterSelected ) { ActiveSound.ApplyRadioFilter( AudioDevice, ParseParams ); } WaveInstance = HandleStart( ActiveSound, NodeWaveInstanceHash); } // Looping sounds are never actually finished if (bLooping || ParseParams.bLooping) { WaveInstance->bIsFinished = false; #if !NO_LOGGING if (!ActiveSound.bWarnedAboutOrphanedLooping && !ActiveSound.AudioComponent.IsValid()) { UE_LOG(LogAudio, Warning, TEXT("Detected orphaned looping sound '%s'."), *ActiveSound.Sound->GetName()); ActiveSound.bWarnedAboutOrphanedLooping = true; } #endif } // Check for finished paths. if( !WaveInstance->bIsFinished ) { // Propagate properties and add WaveInstance to outgoing array of FWaveInstances. WaveInstance->Volume = ParseParams.Volume * Volume; WaveInstance->VolumeMultiplier = ParseParams.VolumeMultiplier; WaveInstance->Pitch = ParseParams.Pitch * Pitch; WaveInstance->HighFrequencyGain = ParseParams.HighFrequencyGain; WaveInstance->bApplyRadioFilter = ActiveSound.bApplyRadioFilter; WaveInstance->StartTime = ParseParams.StartTime; WaveInstance->UserIndex = ActiveSound.UserIndex; WaveInstance->OmniRadius = ParseParams.OmniRadius; bool bAlwaysPlay = false; // Properties from the sound class WaveInstance->SoundClass = ParseParams.SoundClass; if (ParseParams.SoundClass) { FSoundClassProperties* SoundClassProperties = AudioDevice->GetSoundClassCurrentProperties(ParseParams.SoundClass); // Use values from "parsed/ propagated" sound class properties WaveInstance->VolumeMultiplier *= SoundClassProperties->Volume; WaveInstance->Pitch *= SoundClassProperties->Pitch; //TODO: Add in HighFrequencyGainMultiplier property to sound classes WaveInstance->VoiceCenterChannelVolume = SoundClassProperties->VoiceCenterChannelVolume; WaveInstance->RadioFilterVolume = SoundClassProperties->RadioFilterVolume * ParseParams.VolumeMultiplier; WaveInstance->RadioFilterVolumeThreshold = SoundClassProperties->RadioFilterVolumeThreshold * ParseParams.VolumeMultiplier; WaveInstance->StereoBleed = SoundClassProperties->StereoBleed; WaveInstance->LFEBleed = SoundClassProperties->LFEBleed; WaveInstance->bIsUISound = ActiveSound.bIsUISound || SoundClassProperties->bIsUISound; WaveInstance->bIsMusic = ActiveSound.bIsMusic || SoundClassProperties->bIsMusic; WaveInstance->bCenterChannelOnly = ActiveSound.bCenterChannelOnly || SoundClassProperties->bCenterChannelOnly; WaveInstance->bEQFilterApplied = ActiveSound.bEQFilterApplied || SoundClassProperties->bApplyEffects; WaveInstance->bReverb = ActiveSound.bReverb || SoundClassProperties->bReverb; WaveInstance->OutputTarget = SoundClassProperties->OutputTarget; bAlwaysPlay = ActiveSound.bAlwaysPlay || SoundClassProperties->bAlwaysPlay; } else { WaveInstance->VoiceCenterChannelVolume = 0.f; WaveInstance->RadioFilterVolume = 0.f; WaveInstance->RadioFilterVolumeThreshold = 0.f; WaveInstance->StereoBleed = 0.f; WaveInstance->LFEBleed = 0.f; WaveInstance->bEQFilterApplied = ActiveSound.bEQFilterApplied; WaveInstance->bIsUISound = ActiveSound.bIsUISound; WaveInstance->bIsMusic = ActiveSound.bIsMusic; WaveInstance->bReverb = ActiveSound.bReverb; WaveInstance->bCenterChannelOnly = ActiveSound.bCenterChannelOnly; bAlwaysPlay = ActiveSound.bAlwaysPlay; } WaveInstance->PlayPriority = WaveInstance->Volume + ( bAlwaysPlay ? 1.0f : 0.0f ) + WaveInstance->RadioFilterVolume; WaveInstance->Location = ParseParams.Transform.GetTranslation(); WaveInstance->bIsStarted = true; WaveInstance->bAlreadyNotifiedHook = false; WaveInstance->bUseSpatialization = ParseParams.bUseSpatialization; WaveInstance->WaveData = this; WaveInstance->NotifyBufferFinishedHooks = ParseParams.NotifyBufferFinishedHooks; WaveInstance->LoopingMode = ((bLooping || ParseParams.bLooping) ? LOOP_Forever : LOOP_Never); // Don't add wave instances that are not going to be played at this point. if( WaveInstance->PlayPriority > KINDA_SMALL_NUMBER ) { WaveInstances.Add( WaveInstance ); } // We're still alive. ActiveSound.bFinished = false; // Sanity check if( NumChannels > 2 && WaveInstance->bUseSpatialization && !WaveInstance->bReportedSpatializationWarning) { FString SoundWarningInfo = FString::Printf(TEXT("Spatialisation on stereo and multichannel sounds is not supported. SoundWave: %s"), *GetName()); if (ActiveSound.Sound != this) { SoundWarningInfo += FString::Printf(TEXT(" SoundCue: %s"), *ActiveSound.Sound->GetName()); } if (ActiveSound.AudioComponent.IsValid()) { // TODO - Audio Threading. This log would have to be a task back to game thread AActor* SoundOwner = ActiveSound.AudioComponent->GetOwner(); UE_LOG(LogAudio, Warning, TEXT( "%s Actor: %s AudioComponent: %s" ), *SoundWarningInfo, (SoundOwner ? *SoundOwner->GetName() : TEXT("None")), *ActiveSound.AudioComponent->GetName() ); } else { UE_LOG(LogAudio, Warning, TEXT("%s"), *SoundWarningInfo ); } WaveInstance->bReportedSpatializationWarning = true; } } }
void USoundWave::Parse( FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances ) { FWaveInstance* WaveInstance = ActiveSound.FindWaveInstance(NodeWaveInstanceHash); // Create a new WaveInstance if this SoundWave doesn't already have one associated with it. if( WaveInstance == NULL ) { if( !ActiveSound.bRadioFilterSelected ) { ActiveSound.ApplyRadioFilter(ParseParams); } WaveInstance = HandleStart( ActiveSound, NodeWaveInstanceHash); } // Looping sounds are never actually finished if (bLooping || ParseParams.bLooping) { WaveInstance->bIsFinished = false; #if !(NO_LOGGING || UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!ActiveSound.bWarnedAboutOrphanedLooping && ActiveSound.GetAudioComponentID() == 0) { UE_LOG(LogAudio, Warning, TEXT("Detected orphaned looping sound '%s'."), *ActiveSound.GetSound()->GetName()); ActiveSound.bWarnedAboutOrphanedLooping = true; } #endif } // Check for finished paths. if( !WaveInstance->bIsFinished ) { // Propagate properties and add WaveInstance to outgoing array of FWaveInstances. WaveInstance->Volume = ParseParams.Volume * Volume; WaveInstance->VolumeMultiplier = ParseParams.VolumeMultiplier; WaveInstance->VolumeApp = ParseParams.VolumeApp; WaveInstance->Pitch = ParseParams.Pitch * Pitch; WaveInstance->bEnableLowPassFilter = ParseParams.bEnableLowPassFilter; WaveInstance->bIsOccluded = ParseParams.bIsOccluded; WaveInstance->LowPassFilterFrequency = ParseParams.LowPassFilterFrequency; WaveInstance->OcclusionFilterFrequency = ParseParams.OcclusionFilterFrequency; WaveInstance->AttenuationFilterFrequency = ParseParams.AttenuationFilterFrequency; WaveInstance->AmbientZoneFilterFrequency = ParseParams.AmbientZoneFilterFrequency; WaveInstance->bApplyRadioFilter = ActiveSound.bApplyRadioFilter; WaveInstance->StartTime = ParseParams.StartTime; WaveInstance->UserIndex = ActiveSound.UserIndex; WaveInstance->OmniRadius = ParseParams.OmniRadius; WaveInstance->StereoSpread = ParseParams.StereoSpread; WaveInstance->AttenuationDistance = ParseParams.AttenuationDistance; WaveInstance->AbsoluteAzimuth = ParseParams.AbsoluteAzimuth; bool bAlwaysPlay = false; // Properties from the sound class WaveInstance->SoundClass = ParseParams.SoundClass; if (ParseParams.SoundClass) { FSoundClassProperties* SoundClassProperties = AudioDevice->GetSoundClassCurrentProperties(ParseParams.SoundClass); // Use values from "parsed/ propagated" sound class properties WaveInstance->VolumeMultiplier *= SoundClassProperties->Volume; WaveInstance->Pitch *= SoundClassProperties->Pitch; //TODO: Add in HighFrequencyGainMultiplier property to sound classes WaveInstance->VoiceCenterChannelVolume = SoundClassProperties->VoiceCenterChannelVolume; WaveInstance->RadioFilterVolume = SoundClassProperties->RadioFilterVolume * ParseParams.VolumeMultiplier; WaveInstance->RadioFilterVolumeThreshold = SoundClassProperties->RadioFilterVolumeThreshold * ParseParams.VolumeMultiplier; WaveInstance->StereoBleed = SoundClassProperties->StereoBleed; WaveInstance->LFEBleed = SoundClassProperties->LFEBleed; WaveInstance->bIsUISound = ActiveSound.bIsUISound || SoundClassProperties->bIsUISound; WaveInstance->bIsMusic = ActiveSound.bIsMusic || SoundClassProperties->bIsMusic; WaveInstance->bCenterChannelOnly = ActiveSound.bCenterChannelOnly || SoundClassProperties->bCenterChannelOnly; WaveInstance->bEQFilterApplied = ActiveSound.bEQFilterApplied || SoundClassProperties->bApplyEffects; WaveInstance->bReverb = ActiveSound.bReverb || SoundClassProperties->bReverb; WaveInstance->OutputTarget = SoundClassProperties->OutputTarget; if (SoundClassProperties->bApplyAmbientVolumes) { WaveInstance->VolumeMultiplier *= ParseParams.InteriorVolumeMultiplier; WaveInstance->RadioFilterVolume *= ParseParams.InteriorVolumeMultiplier; WaveInstance->RadioFilterVolumeThreshold *= ParseParams.InteriorVolumeMultiplier; } bAlwaysPlay = ActiveSound.bAlwaysPlay || SoundClassProperties->bAlwaysPlay; } else { WaveInstance->VoiceCenterChannelVolume = 0.f; WaveInstance->RadioFilterVolume = 0.f; WaveInstance->RadioFilterVolumeThreshold = 0.f; WaveInstance->StereoBleed = 0.f; WaveInstance->LFEBleed = 0.f; WaveInstance->bEQFilterApplied = ActiveSound.bEQFilterApplied; WaveInstance->bIsUISound = ActiveSound.bIsUISound; WaveInstance->bIsMusic = ActiveSound.bIsMusic; WaveInstance->bReverb = ActiveSound.bReverb; WaveInstance->bCenterChannelOnly = ActiveSound.bCenterChannelOnly; bAlwaysPlay = ActiveSound.bAlwaysPlay; } // If set to bAlwaysPlay, increase the current sound's priority scale by 10x. This will still result in a possible 0-priority output if the sound has 0 actual volume if (bAlwaysPlay) { WaveInstance->Priority = MAX_FLT; } else { WaveInstance->Priority = ParseParams.Priority; } WaveInstance->Location = ParseParams.Transform.GetTranslation(); WaveInstance->bIsStarted = true; WaveInstance->bAlreadyNotifiedHook = false; WaveInstance->bUseSpatialization = ParseParams.bUseSpatialization; WaveInstance->SpatializationAlgorithm = ParseParams.SpatializationAlgorithm; WaveInstance->WaveData = this; WaveInstance->NotifyBufferFinishedHooks = ParseParams.NotifyBufferFinishedHooks; WaveInstance->LoopingMode = ((bLooping || ParseParams.bLooping) ? LOOP_Forever : LOOP_Never); WaveInstance->bIsPaused = ParseParams.bIsPaused; if (AudioDevice->IsHRTFEnabledForAll() && ParseParams.SpatializationAlgorithm == SPATIALIZATION_Default) { WaveInstance->SpatializationAlgorithm = SPATIALIZATION_HRTF; } else { WaveInstance->SpatializationAlgorithm = ParseParams.SpatializationAlgorithm; } // Only append to the wave instances list if we're virtual (always append) or we're audible (non-zero volume) if (WaveInstance->GetVolume() > KINDA_SMALL_NUMBER || (bVirtualizeWhenSilent && AudioDevice->VirtualSoundsEnabled())) { WaveInstances.Add(WaveInstance); } // We're still alive. ActiveSound.bFinished = false; // Sanity check if( NumChannels > 2 && WaveInstance->bUseSpatialization && !WaveInstance->bReportedSpatializationWarning) { static TSet<USoundWave*> ReportedSounds; if (!ReportedSounds.Contains(this)) { FString SoundWarningInfo = FString::Printf(TEXT("Spatialisation on stereo and multichannel sounds is not supported. SoundWave: %s"), *GetName()); if (ActiveSound.GetSound() != this) { SoundWarningInfo += FString::Printf(TEXT(" SoundCue: %s"), *ActiveSound.GetSound()->GetName()); } #if !NO_LOGGING const uint64 AudioComponentID = ActiveSound.GetAudioComponentID(); if (AudioComponentID > 0) { FAudioThread::RunCommandOnGameThread([AudioComponentID, SoundWarningInfo]() { if (UAudioComponent* AudioComponent = UAudioComponent::GetAudioComponentFromID(AudioComponentID)) { AActor* SoundOwner = AudioComponent->GetOwner(); UE_LOG(LogAudio, Warning, TEXT( "%s Actor: %s AudioComponent: %s" ), *SoundWarningInfo, (SoundOwner ? *SoundOwner->GetName() : TEXT("None")), *AudioComponent->GetName() ); } else { UE_LOG(LogAudio, Warning, TEXT("%s"), *SoundWarningInfo ); } }); } else { UE_LOG(LogAudio, Warning, TEXT("%s"), *SoundWarningInfo ); } #endif ReportedSounds.Add(this); } WaveInstance->bReportedSpatializationWarning = true; } } }
bool USoundNodeLooping::NotifyWaveInstanceFinished( FWaveInstance* InWaveInstance ) { FActiveSound* ActiveSound = InWaveInstance->ActiveSound; struct FNodeHashPairs { USoundNode* Node; UPTRINT NodeWaveInstanceHash; FNodeHashPairs(USoundNode* InNode, const UPTRINT InHash) : Node(InNode) , NodeWaveInstanceHash(InHash) { } }; TArray<FNodeHashPairs> NodesToReset; for (int32 ChildNodeIndex = 0; ChildNodeIndex < ChildNodes.Num(); ++ChildNodeIndex) { USoundNode* ChildNode = ChildNodes[ChildNodeIndex]; if (ChildNode) { NodesToReset.Add(FNodeHashPairs(ChildNode, GetNodeWaveInstanceHash(InWaveInstance->NotifyBufferFinishedHooks.GetHashForNode(this), ChildNode, ChildNodeIndex))); } } // GetAllNodes includes current node so we have to start at Index 1. for( int32 ResetNodeIndex = 0; ResetNodeIndex < NodesToReset.Num(); ++ResetNodeIndex ) { const FNodeHashPairs& NodeHashPair = NodesToReset[ResetNodeIndex]; // Reset all child nodes so they are initialized again. uint32* Offset = ActiveSound->SoundNodeOffsetMap.Find( NodeHashPair.NodeWaveInstanceHash ); if( Offset ) { bool* bRequiresInitialization = ( bool* )&ActiveSound->SoundNodeData[ *Offset ]; *bRequiresInitialization = true; } USoundNode* ResetNode = NodeHashPair.Node; if (ResetNode->ChildNodes.Num()) { for (int32 ResetChildIndex = 0; ResetChildIndex < ResetNode->ChildNodes.Num(); ++ResetChildIndex) { USoundNode* ResetChildNode = ResetNode->ChildNodes[ResetChildIndex]; if (ResetChildNode) { NodesToReset.Add(FNodeHashPairs(ResetChildNode, GetNodeWaveInstanceHash(NodeHashPair.NodeWaveInstanceHash, ResetChildNode, ResetChildIndex))); } } } else if (ResetNode->IsA<USoundNodeWavePlayer>()) { FWaveInstance* WaveInstance = ActiveSound->FindWaveInstance(NodeHashPair.NodeWaveInstanceHash); if (WaveInstance) { WaveInstance->bAlreadyNotifiedHook = true; } } } // Reset wave instances that notified us of completion. InWaveInstance->bIsStarted = false; InWaveInstance->bIsFinished = false; return true; }