// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "Components/ActorComponent.h" #include "AudioDecompress.h" // Workers, to have an Async Decompress worker #include "Runtime/Core/Public/Async/AsyncWork.h" #include "AudioDecompressWorker.h" #include "SoundVisComponent.generated.h" USTRUCT(BlueprintType, Blueprintable) struct FSoundVisData { GENERATED_USTRUCT_BODY() UPROPERTY(BlueprintReadOnly, Category = "eXiSoundVis | Sounds") class USoundWave* SoundWaveRef; TSharedPtr PCMData; ~FSoundVisData() { SoundWaveRef = nullptr; PCMData.Reset(); } }; // Delegate that passes FSoundVisData DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFileLoadCompleted, USoundWave*, SoundWaveRef); // Delegate used by the Worker DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWorkerFinished, USoundWave*, SoundWaveRef); // Delegate that passes FrequencyValues DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFrequencySpectrumCalculated, const TArray&, OutFrequencies); UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent), meta=(DisplayName = "SoundVisComponent") ) class EXISOUNDVIS_API USoundVisComponent : public UActorComponent { GENERATED_BODY() /// VARIABLES /// private: // Reference to the SoundWave we are decompressing USoundWave* CompressedSoundWaveRef; /// Blueprint Exposed Variables public: // Audio Component to Play our Audio with UPROPERTY(BlueprintReadOnly, Category = "SoundVis | Sound Player") class UAudioComponent* AudioComponent; // TimerHandle for the SoundPlayer, that only exists to check how long a Sound is running UPROPERTY(BlueprintReadOnly, Category = "SoundVis | Frequency Spectrum") FTimerHandle SoundPlayerTimer; UPROPERTY(BlueprintReadOnly, Category = "SoundVis | Sound Player") bool bSoundPlaying; UPROPERTY(BlueprintReadOnly, Category = "SoundVis | Sound Player") bool bSoundPaused; UPROPERTY(BlueprintReadOnly, Category = "SoundVis | Sound Player") bool bSoundPausedByBackgroundWindow = false; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoundVis | Sound Player") bool bPauseWhenWindowInBackground = true; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SoundVis | Sound Player") bool bNormalizeOutputToDb = false; /// Delegates // Blueprint Delegate that gets Broadcasted when the File is loaded completely UPROPERTY(BlueprintAssignable) FFileLoadCompleted OnFileLoadCompleted; // Blueprint Delegate that gets Broadcasted each time the Frequency Spectrum is calculated UPROPERTY(BlueprintAssignable) FFrequencySpectrumCalculated OnFrequencySpectrumCalculated; /// Debug UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "SoundVis | Debugging") bool bShowLogDebug; UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "SoundVis | Debugging") bool bShowWarningDebug; UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "SoundVis | Debugging") bool bShowErrorDebug; private: // The CurrentSoundData is only valid if the AudioPlayer is paused or running! USoundWave* CurrentSoundData; // The CurrentSegmentLength is only valid if the AudioPlayer is pause or running float CurrentSegmentLength; // Timer and Delegate for the DecompressWorker FTimerHandle AudioDecompressTimer; // Timer and Delegate for the auto calculation of the spectrum FTimerHandle FrequencySpectrumTimer; FTimerDelegate FrequencySpectrumTimerDelegate; /// FUNCTIONS /// public: /// De-/Constructors // Sets default values for this component's properties USoundVisComponent(); // Cleans up stuff ~USoundVisComponent(); /// Overrides // Tick called each frame virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; /// Functions to load Data from the HardDrive // Function to load a sound file from the HD bool LoadSoundFileFromHD(const FString& InFilePath); // Function to fill in the RawFile sound data into the USoundWave object bool FillSoundWaveInfo(class USoundWave* InSoundWave, TArray* InRawFile); /// Function to decompress the compressed Data that comes with the .ogg file bool GetPCMDataFromFile(class USoundWave* InSoundWave); /// Function to calculate the frequency spectrum void CalculateFrequencySpectrum(USoundWave* InSoundWaveRef, const float InStartTime, const float InDuration, TArray& OutFrequencies); /// Helper Functions // Function used to get a better value for the FFT. Uses Hann Window float GetFFTInValue(const int16 InSampleValue, const int16 InSampleIndex, const int16 InSampleCount); // Function to Start a new DecompressTask void InitNewDecompressTask(USoundWave* InSoundWaveRef); // DEBUG Test function to check if Task can call stuff in here void Notify_SoundDecompressed(); void Notify_FailedToDecompress(); // Function that is looped to handle the calculation of the FrequencySpectrum UFUNCTION() void HandleFrequencySpectrumCalculation(); /// Blueprint Versions of the File Data Functions /** * Will load a file (currently .ogg) from your Hard-Drive and save it in a USoundWave variable * * @param InFilePath Absolute path to the File. E.g.: "C:/Sounds/File.ogg" * */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Load Sound File"), Category = "SoundVis | SoundFile") bool BP_LoadSoundFileFromHD(const FString InFilePath); /** * Will get an Array of Names of the Found SoundFiles * * @param InDirectoryPath Path to the Directory in which the Files are (absolute/relative) * @param bInAbsolutePath Tells if the DirectoryPath is absolute (C:/..) or relative to the GameDirectory * @param InFileExtension This is the Extension the Function should look for. For the Plugin it should be .ogg * @param OutSoundFileNamesWithPath The Array of found SoundFileNames (full Path/Name.Extension) * @param OutSoundFileNamesWithoutPath The Array of found SoundFileNames (only Name.Extension) * */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Load Sound File Names"), Category = "SoundVis | SoundFile") void BP_LoadAllSoundFileNamesFromHD(bool& bLoaded, const FString InDirectoryPath, const bool bInAbsolutePath, const FString InFileExtension, TArray& OutSoundFileNamesWithPath, TArray& OutSoundFileNamesWithoutPath); /** * Will call the CalculateFrequencySpectrum function from BP Side * * @param InSoundWave SoundWave that gets analyzed * @param InStartTime The StartPoint of the TimeWindow we want to analyze * @param InDuration The length of the TimeWindow we want to analyze * @param OutFrequencies Array of float values for x Frequencies from 0 to 22000 * */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Calculate Freq Spectrum"), Category = "SoundVis | Frequency Spectrum") void BP_CalculateFrequencySpectrum(USoundWave* InSoundWaveRef, const float InStartTime, const float InDuration, TArray& OutFrequencies); /** * Will play the passed USoundWave and also start calculating the FrequencySpectrum (loops over the InSegmentLength sized parts) * * @param InSoundWaveRef SoundWave that gets started and analyzed * @param InSegmentLength Length of the SoundWave segment that should get analyzed * */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Start Calculate Freq Spectrum"), Category = "SoundVis | Frequency Spectrum") void BP_StartCalculatingFrequencySpectrum(USoundWave* InSoundWaveRef, const float InSegmentLength); /** * If playing, pauses the current playing USoundWave and FrequencySpectrum calculation * */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Pause Calculate Freq Spectrum"), Category = "SoundVis | Frequency Spectrum") void BP_PauseCalculatingFrequencySpectrum(); /** * If playing or paused, stops the current USoundWave and FrequencySpectrum calculation */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Stop Calculate Freq Spectrum"), Category = "SoundVis | Frequency Spectrum") void BP_StopCalculatingFrequencySpectrum(); /** * If paused, resumes the current USoundWave and FrequencySpectrum calculation * */ UFUNCTION(BlueprintCallable, meta = (DisplayName = "Resume Calculate Freq Spectrum"), Category = "SoundVis | Frequency Spectrum") void BP_ResumeCalculatingFrequencySpectrum(); /// Sound Player Information /** * Returns if the Player is currently playing or not * * @return True if playing */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Is Player Playing"), Category = "SoundVis | SoundPlayer") bool IsPlayerPlaying(); /** * Returns if the Player is currently paused or not * * @return True if paused */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Is Player Paused"), Category = "SoundVis | SoundPlayer") bool IsPlayerPaused(); /** * Return the current PlayBack Time of the Sound Player Timer * * @return Current PlayBack Time */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Current Playback Time"), Category = "SoundVis | SoundPlayer") float GetCurrentPlayBackTime(); /// Frequency Data Functions /** * This function will return the value of a specific frequency. It's needs a Frequency Array from the "BP_CalculateFrequencySpectrum" function and the matching SoundWave * * @param InSoundWave SoundWave to get specific data from (SampleRate) * @param InFrequencies Array of float values for different frequencies from 0 to 22000. Can be get by using the "BP_CalculateFrequencySpectrum" function * @param InWantedFrequency The Frequency of which you want the value of * @param OutFrequencyValue Float value of the requested frequency * */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Freq Value"), Category = "SoundVis | Frequency Values") static void BP_GetSpecificFrequencyValue(USoundWave* InSoundWave, TArray InFrequencies, int32 InWantedFrequency, float& OutFrequencyValue); /** * This function will return the average value for SubBass (20 to 60hz) * * @param InSoundWave SoundWave to get specific data from (SampleRate) * @param InFrequencies Array of float values for different frequencies from 0 to 22000. Can be get by using the "BP_CalculateFrequencySpectrum" function * @param OutAverageSubBass Average value of the frequencies from 20 to 60 * */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Average Subbass Value"), Category = "SoundVis | Frequency Values") static void BP_GetAverageSubBassValue(USoundWave* InSoundWave, TArray InFrequencies, float& OutAverageSubBass); /** * This function will return the average value for Bass (60 to 250hz) * * @param InSoundWave SoundWave to get specific data from (SampleRate) * @param InFrequencies Array of float values for different frequencies from 0 to 22000. Can be get by using the "BP_CalculateFrequencySpectrum" function * @param OutAverageBass Average value of the frequencies from 60 to 250 * */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Average Bass Value"), Category = "SoundVis | Frequency Values") static void BP_GetAverageBassValue(USoundWave* InSoundWave, TArray InFrequencies, float& OutAverageBass); /** * This function will return the average value for a given frequency interval e.g.: 20 to 60 (SubBass) * * @param InSoundWave SoundWave to get specific data from (SampleRate) * @param InFrequencies Array of float values for different frequencies from 0 to 22000. Can be get by using the "BP_CalculateFrequencySpectrum" function * @param InStartFrequency Start Frequency of the Frequency interval * @param InEndFrequency End Frequency of the Frequency interval * @param OutAverageFrequency Average value of the requested frequency interval * */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Average Freq Value In Range"), Category = "SoundVis | Frequency Values") static void BP_GetAverageFrequencyValueInRange(USoundWave* InSoundWave, TArray InFrequencies, int32 InStartFrequence, int32 InEndFrequence, float& OutAverageFrequency); };