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;
}