float FVolumeManager::GetVolumeAttenuation(uint32 VoiceDataIndex) const { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); return Attenuation[VoiceDataIndex]; }
float FVolumeManager::GetVolumeFade(uint32 VoiceDataIndex) const { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); return FadeScale.CurrentValue[VoiceDataIndex]; }
float FVolumeManager::GetVolumeProduct(uint32 VoiceDataIndex) const { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); return VolumeProduct[VoiceDataIndex]; }
void FVolumeManager::SetDynamicVolumeScale(uint32 VoiceDataIndex, float InVolume, float DeltaTimeSec) { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); DEBUG_AUDIO_CHECK(InVolume >= 0.0f && InVolume <= 1.0f); DynamicScale.SetValue(VoiceDataIndex, InVolume, LastTimeSec, DeltaTimeSec); }
void FVolumeManager::Update() { double CurrentTimeSec = AudioModule->GetCurrentTimeSec(); LastTimeSec = CurrentTimeSec; // If there are no volumes to update, then no need to update anything if (EntryCount == 0) { return; } int32 UpdateCount = 0; float DeltaTime = 0.0f; float Fraction = 0.0f; float Result = 0.0f; int32 TotalCount = Baseline.Num(); // Update all dynamic volume parameters in one parallelized loop. // TODO: implement SSE for (int32 Index = 0; Index < TotalCount && UpdateCount < EntryCount; ++Index) { // If the value of baseline is less than 0.0f, it's been a released volume entry if (Baseline[Index] < 0.0f) { continue; } ++UpdateCount; float Product = Baseline[Index] * Attenuation[Index] * DynamicScale.Compute(Index, CurrentTimeSec) * FadeScale.Compute(Index, CurrentTimeSec); DEBUG_AUDIO_CHECK(Product >= 0.0f && Product <= 1.0f); VolumeProduct[Index] = Product; } // Reset the volume priority sorter SortedVoices.Reset(); UpdateCount = 0; VoiceComparePredicate Predicate; for (int32 Index = 0; Index < TotalCount && UpdateCount < EntryCount; ++Index) { if (Baseline[Index] < 0.0f) { continue; } ++UpdateCount; float Temp = VolumeProduct[Index] * PriorityWeight[Index]; VolumeWeightedPriority[Index] = Temp; SortedVoices.HeapPush(FSortedVoiceEntry(Index, Temp), Predicate); } // Make sure we updated everything DEBUG_AUDIO_CHECK(UpdateCount == EntryCount); }
void FVolumeManager::ReleaseEntry(uint32 VoiceDataIndex) { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); --EntryCount; DEBUG_AUDIO_CHECK(EntryCount >= 0); // Setting baseline to -1.0f is releasing the volume data at that index Baseline[VoiceDataIndex] = -1.0f; }
void FVolumeManager::InitializeEntry(uint32 VoiceDataIndex, const FVolumeInitParam& Params) { ++EntryCount; DEBUG_AUDIO_CHECK(EntryCount< Baseline.Num()); DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Params.VolumeProduct >= 0.0f && Params.VolumeProduct <= 1.0f); Baseline[VoiceDataIndex] = Params.BaseVolume; Attenuation[VoiceDataIndex] = Params.VolumeAttenuation; VolumeProduct[VoiceDataIndex] = Params.VolumeProduct; EmitterHandle[VoiceDataIndex] = Params.EmitterHandle; PriorityWeight[VoiceDataIndex] = Params.PriorityWeight; DynamicScale.InitEntry(VoiceDataIndex); DynamicScale.SetValue(VoiceDataIndex, Params.VolumeScale, LastTimeSec, Params.VolumeScaleDeltaTime); FadeScale.InitEntry(VoiceDataIndex); }
void FDynamicParamData::InitEntry(uint32 Index) { DEBUG_AUDIO_CHECK(Index < (uint32)StartValue.Num()); StartValue[Index] = 1.0f; EndValue[Index] = 1.0f; CurrentValue[Index] = 1.0f; StartTime[Index] = 0.0f; DeltaTime[Index] = 0.0f; bIsDone[Index] = true; }
void FVoice::NotifyPlayVirtual(uint32 InVoiceDataIndex) { DEBUG_AUDIO_CHECK(InVoiceDataIndex != INDEX_NONE); VoiceDataIndex = InVoiceDataIndex; PlayingState = EVoicePlayingState::PLAYING_VIRTUAL; for (int32 i = 0; i < VoiceListeners.Num(); ++i) { VoiceListeners[i]->OnVoiceVirtual(this); } }
EVoiceError::Type FVoice::GetVolumeProduct(float& OutVolumeProduct) const { AUDIO_VOICE_CHECK_ERROR(); if (IsPlaying()) { DEBUG_AUDIO_CHECK(VoiceDataIndex != INDEX_NONE); FVoiceManager& VoiceManager = AudioModule->GetVoiceManager(); return VoiceManager.GetVolumeProduct(VoiceDataIndex, OutVolumeProduct); } OutVolumeProduct = 1.0f; return EVoiceError::VOICE_NOT_PLAYING; }
EVoiceError::Type FVoice::SetPitchScale(float InPitchScale, float InDeltaTimeSec /* = 0.0f */) { AUDIO_VOICE_CHECK_ERROR(); AUDIO_VOICE_CHECK_SUSPEND(); if (PlayInfo.PitchScale != InPitchScale || PlayInfo.PitchScaletime != InDeltaTimeSec) { PlayInfo.PitchScale = InPitchScale; PlayInfo.PitchScaletime = InDeltaTimeSec; if (IsPlaying()) { DEBUG_AUDIO_CHECK(VoiceDataIndex != INDEX_NONE); FVoiceManager& VoiceManager = AudioModule->GetVoiceManager(); return SetError(VoiceManager.SetPitchScale(VoiceDataIndex, InPitchScale, InDeltaTimeSec)); } } return EVoiceError::NONE; }
void DoWork() { // Synchronously load the input sound file TSharedPtr<ISoundFile> InputSoundFile = AudioModule->LoadSoundFile(*SoundFilePath, false); if (!InputSoundFile.IsValid()) { return; } TSharedPtr<FSoundFileReader> InputSoundFileReader = AudioModule->CreateSoundFileReader(); ESoundFileError::Type Error = InputSoundFileReader->Init(InputSoundFile, false); if (Error != ESoundFileError::NONE) { return; } check(Error == ESoundFileError::NONE); // Cast to an internal object to get access to non-public API FSoundFile* InputSoundFileInternal = static_cast<FSoundFile*>(InputSoundFile.Get()); FSoundFileDescription InputDescription; Error = InputSoundFileInternal->GetDescription(InputDescription); check(Error == ESoundFileError::NONE); TArray<ESoundFileChannelMap::Type> ChannelMap; Error = InputSoundFileInternal->GetChannelMap(ChannelMap); FSoundFileDescription NewSoundFileDescription; NewSoundFileDescription.NumChannels = InputDescription.NumChannels; NewSoundFileDescription.NumFrames = InputDescription.NumFrames; NewSoundFileDescription.FormatFlags = ConvertFormat.Format; NewSoundFileDescription.SampleRate = ConvertFormat.SampleRate; NewSoundFileDescription.NumSections = InputDescription.NumSections; NewSoundFileDescription.bIsSeekable = InputDescription.bIsSeekable; TSharedPtr<FSoundFileWriter> SoundFileWriter = AudioModule->CreateSoundFileWriter(); Error = SoundFileWriter->Init(NewSoundFileDescription, ChannelMap, ConvertFormat.EncodingQuality); check(Error == ESoundFileError::NONE); // Create a buffer to do the processing SoundFileCount ProcessBufferSamples = 1024 * NewSoundFileDescription.NumChannels; TArray<float> ProcessBuffer; ProcessBuffer.Init(0.0f, ProcessBufferSamples); double SampleRateConversionRatio = (double)InputDescription.SampleRate / ConvertFormat.SampleRate; FSampleRateConverter SampleRateConverter; SampleRateConverter.Init(SampleRateConversionRatio, NewSoundFileDescription.NumChannels); TArray<float> OutputBuffer; SoundFileCount OutputBufferSamples = ProcessBufferSamples / SampleRateConversionRatio; OutputBuffer.Reserve(OutputBufferSamples); // Find the max value if we've been told to do peak normalization on import float MaxValue = 0.0f; SoundFileCount InputSamplesRead = 0; bool bPerformPeakNormalization = ConvertFormat.bPerformPeakNormalization; if (bPerformPeakNormalization) { Error = InputSoundFileReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead); SOUND_CONVERT_CHECK(Error); while (InputSamplesRead) { for (SoundFileCount Sample = 0; Sample < InputSamplesRead; ++Sample) { if (ProcessBuffer[Sample] > FMath::Abs(MaxValue)) { MaxValue = ProcessBuffer[Sample]; } } Error = InputSoundFileReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead); SOUND_CONVERT_CHECK(Error); } // If this happens, it means we have a totally silent file if (MaxValue == 0.0) { bPerformPeakNormalization = false; } // Seek the file back to the beginning SoundFileCount OutOffset; InputSoundFileReader->SeekFrames(0, ESoundFileSeekMode::FROM_START, OutOffset); } bool SamplesProcessed = true; // Read the first block of samples Error = InputSoundFileReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead); SOUND_CONVERT_CHECK(Error); while (InputSamplesRead != 0) { SampleRateConverter.ProcessBlock(ProcessBuffer.GetData(), InputSamplesRead, OutputBuffer); SoundFileCount SamplesWritten; Error = SoundFileWriter->WriteSamples((const float*)OutputBuffer.GetData(), OutputBuffer.Num(), SamplesWritten); SOUND_CONVERT_CHECK(Error); DEBUG_AUDIO_CHECK(SamplesWritten == OutputBuffer.Num()); OutputBuffer.Reset(); // read more samples Error = InputSoundFileReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead); SOUND_CONVERT_CHECK(Error); // ... normalize the samples if we're told to if (bPerformPeakNormalization) { for (int32 Sample = 0; Sample < InputSamplesRead; ++Sample) { ProcessBuffer[Sample] /= MaxValue; } } } // Release the sound file handles as soon as we finished converting the file InputSoundFileReader->Release(); SoundFileWriter->Release(); // Get the raw binary data TArray<uint8>* Data = nullptr; SoundFileWriter->GetData(&Data); // Write the data to the output file bool bSuccess = FFileHelper::SaveArrayToFile(*Data, *OutSoundFilePath); check(bSuccess); }
FVoice::~FVoice() { DEBUG_AUDIO_CHECK(VoiceDataIndex == INDEX_NONE); }
bool FVolumeManager::IsFadedOut(uint32 VoiceDataIndex) const { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); return FadeScale.EndValue[VoiceDataIndex] == 0.0f && FadeScale.bIsDone[VoiceDataIndex]; }
void FVolumeManager::SetFadeOut(uint32 VoiceDataIndex, float FadeTimeSec) { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); FadeScale.SetValue(VoiceDataIndex, 0.0f, LastTimeSec, FadeTimeSec); }
void FVolumeManager::SetAttenuation(uint32 VoiceDataIndex, float InAttenuation) { DEBUG_AUDIO_CHECK(VoiceDataIndex < (uint32)Baseline.Num()); DEBUG_AUDIO_CHECK(Baseline[VoiceDataIndex] > 0.0f); Attenuation[VoiceDataIndex] = InAttenuation; }